From 7a9f55affef340ae6083e0729d729a979f9b035f Mon Sep 17 00:00:00 2001 From: Ziheng Sun Date: Wed, 26 Aug 2026 23:35:41 -0400 Subject: [PATCH 01/10] Upgrade Geoweaver to Spring Boot 3.3 and Java 17. Migrate from Boot 2.4/javax to Boot 3.3/jakarta so the app runs on modern JDKs used by CI and developer machines, replacing Springfox with springdoc and fixing Boot-3 security and circular-reference startup issues. Co-authored-by: Cursor --- pom.xml | 395 +++++------------- .../java/com/gw/GeoweaverApplication.java | 22 - .../com/gw/database/CheckpointRepository.java | 2 +- .../gw/database/EnvironmentRepository.java | 2 +- .../com/gw/database/HistoryRepository.java | 2 +- .../java/com/gw/database/HostRepository.java | 2 +- .../gw/database/LogActivityRepository.java | 2 +- .../com/gw/database/ProcessRepository.java | 2 +- .../java/com/gw/database/UserRepository.java | 2 +- .../com/gw/database/WorkflowRepository.java | 2 +- src/main/java/com/gw/jpa/Checkpoint.java | 35 +- src/main/java/com/gw/jpa/Environment.java | 2 +- src/main/java/com/gw/jpa/GWProcess.java | 6 +- src/main/java/com/gw/jpa/GWUser.java | 6 +- src/main/java/com/gw/jpa/History.java | 8 +- src/main/java/com/gw/jpa/HistoryDTO.java | 4 +- src/main/java/com/gw/jpa/Host.java | 10 +- src/main/java/com/gw/jpa/LogActivity.java | 4 +- src/main/java/com/gw/jpa/Workflow.java | 6 +- .../java/com/gw/local/LocalSessionOutput.java | 2 +- .../java/com/gw/server/CommandServlet.java | 18 +- .../java/com/gw/server/FileUploadServlet.java | 188 ++------- .../java/com/gw/server/TerminalServlet.java | 16 +- .../com/gw/server/Test2SocketServlet.java | 16 +- .../java/com/gw/server/TestSocketServlet.java | 14 +- .../gw/server/WebsocketMessageHandler.java | 4 +- .../java/com/gw/server/WorkflowServlet.java | 18 +- .../java/com/gw/ssh/SSHCmdSessionOutput.java | 2 +- .../java/com/gw/ssh/SSHLiveSessionOutput.java | 2 +- .../com/gw/ssh/SecurityConfiguration.java | 65 ++- .../com/gw/tasks/GeoweaverProcessTask.java | 8 +- .../com/gw/tasks/GeoweaverWorkflowTask.java | 2 +- src/main/java/com/gw/tasks/Task.java | 2 +- src/main/java/com/gw/tools/BuiltinTool.java | 2 +- src/main/java/com/gw/tools/SessionPair.java | 2 +- src/main/java/com/gw/tools/UserTool.java | 2 +- src/main/java/com/gw/utils/BaseTool.java | 2 +- src/main/java/com/gw/utils/HttpUtil.java | 2 +- .../com/gw/web/FileDownloadController.java | 4 +- .../java/com/gw/web/GeoweaverController.java | 10 +- .../com/gw/web/GoogleEarthController.java | 60 +-- .../java/com/gw/web/LoginInterceptor.java | 8 +- .../java/com/gw/web/PortalController.java | 2 +- src/main/java/com/gw/web/UserController.java | 4 +- .../java/com/gw/workers/WorkerManager.java | 3 +- src/main/resources/application.properties | 4 + .../resources/templates/reset_password.html | 2 +- .../com/gw/AbstractHelperMethodsTest.java | 4 +- src/test/java/com/gw/BaseToolTest.java | 2 +- src/test/java/com/gw/EnvironmentTest.java | 2 +- src/test/java/com/gw/FileTest.java | 2 +- src/test/java/com/gw/GeneralTests.java | 2 +- .../java/com/gw/GeoweaverControllerTest.java | 2 +- .../java/com/gw/GoogleEarthEngineTest.java | 2 +- src/test/java/com/gw/HistoryTest.java | 2 +- src/test/java/com/gw/HostGeneralTest.java | 2 +- src/test/java/com/gw/LocalSessionTests.java | 12 +- src/test/java/com/gw/OtherFunctionalTest.java | 2 +- src/test/java/com/gw/ProcessBuiltinTest.java | 2 +- src/test/java/com/gw/ProcessGeneralTest.java | 2 +- src/test/java/com/gw/ProcessJupyterTest.java | 2 +- src/test/java/com/gw/ProcessPythonTest.java | 2 +- src/test/java/com/gw/ProcessShellTest.java | 2 +- src/test/java/com/gw/SSHSessionTest.java | 2 +- src/test/java/com/gw/ServletTest.java | 10 +- src/test/java/com/gw/UserTest.java | 2 +- src/test/java/com/gw/UtilsTest.java | 2 +- src/test/java/com/gw/WebsocketTest.java | 4 +- src/test/java/com/gw/WorkflowTest.java | 2 +- .../java/com/gw/jpa/JPAAnnotationsTest.java | 15 +- .../com/gw/server/CommandServletTest.java | 6 +- .../com/gw/server/FileUploadServletTest.java | 90 +--- .../com/gw/server/TerminalServletTest.java | 6 +- .../com/gw/server/WorkflowServletTest.java | 6 +- src/test/java/com/gw/tools/UserToolTest.java | 2 +- src/test/java/com/gw/utils/HttpUtilTest.java | 2 +- .../com/gw/web/AsyncExportControllerTest.java | 4 +- 77 files changed, 370 insertions(+), 805 deletions(-) diff --git a/pom.xml b/pom.xml index 28cc91620..31c8fa7b1 100644 --- a/pom.xml +++ b/pom.xml @@ -5,38 +5,26 @@ org.springframework.boot spring-boot-starter-parent - 2.4.1 - - + 3.3.6 + com.gw geoweaver - 2.1.7 + 2.2.0-SNAPSHOT geoweaver - A lightweight workflow management software for organizing data analysis workflows, - preserving history of every workflow run, and improving scientist producitvity and workflow FAIRness, - especially for the AI experiments which have no two identifical runs. - + A lightweight workflow management software for organizing data analysis workflows, + preserving history of every workflow run, and improving scientist productivity and workflow FAIRness, + especially for the AI experiments which have no two identical runs. + jar - - 5.3.32 - - 4.6.3 - - 2.4.1 - - 3.0.12.RELEASE - + 17 com.gw.GeoweaverApplication - - 2.17.1 - - 4.13.2 - + 4.7.6 + 2.6.0 - + org.springframework.boot @@ -45,18 +33,7 @@ org.springframework.boot spring-boot-starter-security - - - org.springframework.boot - spring-boot-starter-logging - - - org.slf4j - log4j-over-slf4j - - - - + org.springframework.boot spring-boot-starter-thymeleaf @@ -65,32 +42,45 @@ org.springframework.boot spring-boot-starter-web - + + org.springframework.boot + spring-boot-starter-websocket + + + + org.slf4j + log4j-over-slf4j + + + org.dom4j + dom4j + 2.1.4 + + + org.thymeleaf.extras + thymeleaf-extras-springsecurity6 + org.postgresql postgresql - 42.7.2 + runtime - com.mysql mysql-connector-j - 8.3.0 + runtime - - - org.projectlombok - lombok - 1.18.30 - provided + com.h2database + h2 + 2.2.224 - - org.thymeleaf.extras - thymeleaf-extras-springsecurity5 + org.projectlombok + lombok + true @@ -104,71 +94,22 @@ test - org.springframework.boot - spring-boot-devtools - false - - - - - - - jakarta.servlet - jakarta.servlet-api - 6.0.0 - - - - - - junit - junit - test - - - org.junit.jupiter - junit-jupiter-api - 5.9.1 - test - - - - io.springfox - springfox-swagger2 - 3.0.0 + org.springframework.boot + spring-boot-devtools + true + - io.springfox - springfox-boot-starter - 3.0.0 + org.springdoc + springdoc-openapi-starter-webmvc-ui + ${springdoc.version} - - org.hamcrest - hamcrest-core - 2.2 - test - - - org.kamranzafar - jtar - 2.3 - - - aopalliance - aopalliance - 1.0 - - - javax.mail - mail - 1.4.5 + org.kamranzafar + jtar + 2.3 com.google.apis @@ -178,217 +119,72 @@ org.jsoup jsoup - 1.15.3 + 1.17.2 - org.json - json - 20231013 + org.json + json + 20240303 org.apache.commons commons-text - 1.10.0 + 1.12.0 - info.picocli picocli-spring-boot-starter ${picocli-version} - - org.springframework - spring-aop - ${org.springframework-version} - - - org.springframework - spring-beans - ${org.springframework-version} - - - org.springframework - spring-context - ${org.springframework-version} - - - org.springframework - spring-core - ${org.springframework-version} - - - org.springframework - spring-expression - ${org.springframework-version} - - - org.springframework - spring-web - ${org.springframework-version} - - - org.springframework - spring-webmvc - ${org.springframework-version} - - - - org.springframework - spring-websocket - ${org.springframework-version} - - - - org.springframework.boot - spring-boot-starter-websocket - ${org.springboot-version} - - - org.springframework - spring-messaging - ${org.springframework-version} - - - - org.thymeleaf - thymeleaf - ${thymeleaf-version} - - - org.thymeleaf - thymeleaf-spring5 - ${thymeleaf-version} - - - commons-codec - commons-codec - 1.3 + commons-codec + commons-codec - - - commons-httpclient - commons-httpclient - 3.1 - - - com.googlecode.json-simple - json-simple - 1.1.1 - - - org.slf4j - slf4j-reload4j - 1.7.36 - - - - commons-io - commons-io - [2.7,) - - - it.sauronsoftware.cron4j - cron4j - 2.2.5 - - - commons-fileupload - commons-fileupload - 1.5 - - - - - - org.jvnet.jaxb2_commons - jaxb2-basics-runtime - 1.11.1 - - - com.hierynomus - sshj - 0.33.0 - - - net.sf.expectit - expectit-core - 0.6.1 - - - javax.el - javax.el-api - 2.2.4 - - - com.h2database - h2 - 2.2.224 - - - - - org.apache.httpcomponents - httpclient - - - - info.picocli - picocli - 4.6.2 + commons-httpclient + commons-httpclient + 3.1 - - - javax.websocket - javax.websocket-api - 1.0 + + com.googlecode.json-simple + json-simple + 1.1.1 - - javax.websocket - javax.websocket-client-api - 1.0 + commons-io + commons-io + 2.16.1 - - org.glassfish.tyrus.bundles - tyrus-standalone-client - 1.17 + it.sauronsoftware.cron4j + cron4j + 2.2.5 - - org.apache.tomcat - juli - 6.0.26 + com.hierynomus + sshj + 0.38.0 - - com.github.oshi - oshi-core - 6.0.0 + net.sf.expectit + expectit-core + 0.9.0 - - - - javax.xml.soap - javax.xml.soap-api - 1.4.0 + org.apache.httpcomponents + httpclient + 4.5.14 - - javax.xml.bind - jaxb-api - 2.3.1 + com.github.oshi + oshi-core + 6.6.5 - + - javax.annotation - javax.annotation-api - 1.3.2 + org.eclipse.angus + angus-mail - @@ -397,10 +193,16 @@ org.springframework.boot spring-boot-maven-plugin - - ${project.artifactId} - - + + ${project.artifactId} + + + + org.projectlombok + lombok + + + build-info @@ -440,25 +242,18 @@ org.apache.maven.plugins - maven-resources-plugin - 3.1.0 - - maven-compiler-plugin - 3.11.0 - 11 + ${java.version} org.projectlombok lombok - 1.18.30 + ${lombok.version} - - diff --git a/src/main/java/com/gw/GeoweaverApplication.java b/src/main/java/com/gw/GeoweaverApplication.java index ece20b7ca..6042915ed 100644 --- a/src/main/java/com/gw/GeoweaverApplication.java +++ b/src/main/java/com/gw/GeoweaverApplication.java @@ -1,6 +1,5 @@ package com.gw; -import com.google.api.client.util.Value; import com.gw.jpa.GWUser; import com.gw.jpa.Host; import com.gw.tools.HostTool; @@ -11,8 +10,6 @@ import java.awt.Desktop; import java.io.File; import java.io.IOException; -import java.net.InetSocketAddress; -import java.net.Socket; import java.net.URI; import java.net.URISyntaxException; import org.apache.log4j.Logger; @@ -23,23 +20,13 @@ import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.boot.web.servlet.ServletComponentScan; import org.springframework.context.ApplicationContext; -import org.springframework.context.annotation.Bean; -import springfox.documentation.builders.PathSelectors; -import springfox.documentation.builders.RequestHandlerSelectors; -import springfox.documentation.spi.DocumentationType; -import springfox.documentation.spring.web.plugins.Docket; -import springfox.documentation.swagger2.annotations.EnableSwagger2; @SpringBootApplication @ServletComponentScan -@EnableSwagger2 public class GeoweaverApplication { static Logger logger = Logger.getLogger(GeoweaverApplication.class); - @Value("${geoweaver.workspace}") - private static String workspace; - public static void main(String[] args) { // Create log directory before anything else to avoid NoSuchFileException createLogDirectory(); @@ -104,15 +91,6 @@ private static void createLogDirectory() { } } - @Bean - public Docket geoweaverAPI() { - return new Docket(DocumentationType.SWAGGER_2) - .select() - .apis(RequestHandlerSelectors.any()) - .paths(PathSelectors.any()) - .build(); - } - public static void show_ascii_art(){ // ANSI escape codes for colors String reset = "\u001B[0m"; diff --git a/src/main/java/com/gw/database/CheckpointRepository.java b/src/main/java/com/gw/database/CheckpointRepository.java index 0a8b0f7d0..4f2f57ba4 100644 --- a/src/main/java/com/gw/database/CheckpointRepository.java +++ b/src/main/java/com/gw/database/CheckpointRepository.java @@ -7,7 +7,7 @@ import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; -import javax.transaction.Transactional; +import jakarta.transaction.Transactional; @Transactional public interface CheckpointRepository extends JpaRepository { diff --git a/src/main/java/com/gw/database/EnvironmentRepository.java b/src/main/java/com/gw/database/EnvironmentRepository.java index 21a6500e1..14907ff3c 100644 --- a/src/main/java/com/gw/database/EnvironmentRepository.java +++ b/src/main/java/com/gw/database/EnvironmentRepository.java @@ -5,7 +5,7 @@ import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.CrudRepository; -import javax.transaction.Transactional; +import jakarta.transaction.Transactional; /** * The EnvironmentRepository interface provides methods for querying environment configurations from diff --git a/src/main/java/com/gw/database/HistoryRepository.java b/src/main/java/com/gw/database/HistoryRepository.java index cdad97456..834900f49 100644 --- a/src/main/java/com/gw/database/HistoryRepository.java +++ b/src/main/java/com/gw/database/HistoryRepository.java @@ -9,7 +9,7 @@ import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; -import javax.transaction.Transactional; +import jakarta.transaction.Transactional; /** * The HistoryRepository interface provides methods for querying historical execution data (history) diff --git a/src/main/java/com/gw/database/HostRepository.java b/src/main/java/com/gw/database/HostRepository.java index e16075e38..3898e43c3 100644 --- a/src/main/java/com/gw/database/HostRepository.java +++ b/src/main/java/com/gw/database/HostRepository.java @@ -6,7 +6,7 @@ import org.springframework.data.repository.CrudRepository; import org.springframework.data.repository.query.Param; -import javax.transaction.Transactional; +import jakarta.transaction.Transactional; /** * The HostRepository interface provides methods for querying host-related diff --git a/src/main/java/com/gw/database/LogActivityRepository.java b/src/main/java/com/gw/database/LogActivityRepository.java index cd1ff33cf..e77e3abfe 100644 --- a/src/main/java/com/gw/database/LogActivityRepository.java +++ b/src/main/java/com/gw/database/LogActivityRepository.java @@ -3,7 +3,7 @@ import com.gw.jpa.LogActivity; import org.springframework.data.repository.CrudRepository; -import javax.transaction.Transactional; +import jakarta.transaction.Transactional; /** Log Activity Repository */ @Transactional diff --git a/src/main/java/com/gw/database/ProcessRepository.java b/src/main/java/com/gw/database/ProcessRepository.java index 78c89a1f3..4dbfb16c3 100644 --- a/src/main/java/com/gw/database/ProcessRepository.java +++ b/src/main/java/com/gw/database/ProcessRepository.java @@ -6,7 +6,7 @@ import org.springframework.data.repository.CrudRepository; import org.springframework.data.repository.query.Param; -import javax.transaction.Transactional; +import jakarta.transaction.Transactional; /** * The ProcessRepository interface provides methods for querying process information from a diff --git a/src/main/java/com/gw/database/UserRepository.java b/src/main/java/com/gw/database/UserRepository.java index 9b360fe37..9da46c199 100644 --- a/src/main/java/com/gw/database/UserRepository.java +++ b/src/main/java/com/gw/database/UserRepository.java @@ -3,7 +3,7 @@ import com.gw.jpa.GWUser; import org.springframework.data.repository.CrudRepository; -import javax.transaction.Transactional; +import jakarta.transaction.Transactional; @Transactional public interface UserRepository extends CrudRepository {} diff --git a/src/main/java/com/gw/database/WorkflowRepository.java b/src/main/java/com/gw/database/WorkflowRepository.java index 673135e5f..df68be1df 100644 --- a/src/main/java/com/gw/database/WorkflowRepository.java +++ b/src/main/java/com/gw/database/WorkflowRepository.java @@ -6,7 +6,7 @@ import org.springframework.data.repository.CrudRepository; import org.springframework.data.repository.query.Param; -import javax.transaction.Transactional; +import jakarta.transaction.Transactional; /** * The WorkflowRepository interface provides methods for querying workflow information from a diff --git a/src/main/java/com/gw/jpa/Checkpoint.java b/src/main/java/com/gw/jpa/Checkpoint.java index 8ff0fdae8..6b904b710 100644 --- a/src/main/java/com/gw/jpa/Checkpoint.java +++ b/src/main/java/com/gw/jpa/Checkpoint.java @@ -1,22 +1,23 @@ package com.gw.jpa; -import javax.persistence.CascadeType; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.FetchType; -import javax.persistence.GeneratedValue; -import javax.persistence.Id; -import javax.persistence.JoinColumn; -import javax.persistence.Lob; -import javax.persistence.ManyToOne; -import javax.persistence.PrePersist; -import javax.persistence.Table; +import jakarta.persistence.CascadeType; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.Lob; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.PrePersist; +import jakarta.persistence.Table; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; -import org.hibernate.annotations.GenericGenerator; -import org.hibernate.annotations.Type; +import org.hibernate.annotations.JdbcTypeCode; +import org.hibernate.annotations.UuidGenerator; +import org.hibernate.type.SqlTypes; import java.util.Date; import java.util.UUID; @@ -29,10 +30,10 @@ public class Checkpoint { @Id - @GeneratedValue(generator = "uuid2") - @GenericGenerator(name = "uuid2", strategy = "uuid2") - @Type(type = "uuid-char") - @Column(name = "id", columnDefinition = "VARCHAR(36)") + @GeneratedValue + @UuidGenerator + @JdbcTypeCode(SqlTypes.CHAR) + @Column(name = "id", length = 36) private UUID id; @Column(name = "executionId") diff --git a/src/main/java/com/gw/jpa/Environment.java b/src/main/java/com/gw/jpa/Environment.java index e024fcbe8..90dc1ab62 100644 --- a/src/main/java/com/gw/jpa/Environment.java +++ b/src/main/java/com/gw/jpa/Environment.java @@ -1,6 +1,6 @@ package com.gw.jpa; -import javax.persistence.*; +import jakarta.persistence.*; import com.fasterxml.jackson.annotation.JsonProperty; import lombok.Getter; diff --git a/src/main/java/com/gw/jpa/GWProcess.java b/src/main/java/com/gw/jpa/GWProcess.java index 9a341469a..5ecc57998 100644 --- a/src/main/java/com/gw/jpa/GWProcess.java +++ b/src/main/java/com/gw/jpa/GWProcess.java @@ -4,9 +4,9 @@ import lombok.NoArgsConstructor; import lombok.Setter; -import javax.persistence.Lob; -import javax.persistence.Entity; -import javax.persistence.Id; +import jakarta.persistence.Lob; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; /** * Process POJO diff --git a/src/main/java/com/gw/jpa/GWUser.java b/src/main/java/com/gw/jpa/GWUser.java index 47a3f3eb9..79a384916 100644 --- a/src/main/java/com/gw/jpa/GWUser.java +++ b/src/main/java/com/gw/jpa/GWUser.java @@ -2,9 +2,9 @@ import java.util.Date; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.Id; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; import lombok.Getter; import lombok.NoArgsConstructor; diff --git a/src/main/java/com/gw/jpa/History.java b/src/main/java/com/gw/jpa/History.java index b88fb8fb5..193502d68 100644 --- a/src/main/java/com/gw/jpa/History.java +++ b/src/main/java/com/gw/jpa/History.java @@ -4,10 +4,10 @@ import lombok.Data; -import javax.persistence.Column; -import javax.persistence.Lob; -import javax.persistence.Entity; -import javax.persistence.Id; +import jakarta.persistence.Column; +import jakarta.persistence.Lob; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; import java.util.Date; @Entity diff --git a/src/main/java/com/gw/jpa/HistoryDTO.java b/src/main/java/com/gw/jpa/HistoryDTO.java index 54ac67468..180da0f9d 100644 --- a/src/main/java/com/gw/jpa/HistoryDTO.java +++ b/src/main/java/com/gw/jpa/HistoryDTO.java @@ -2,8 +2,8 @@ import java.util.Date; -import javax.persistence.Entity; -import javax.persistence.Id; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; import lombok.Data; diff --git a/src/main/java/com/gw/jpa/Host.java b/src/main/java/com/gw/jpa/Host.java index f52dfdee1..f3db594a4 100644 --- a/src/main/java/com/gw/jpa/Host.java +++ b/src/main/java/com/gw/jpa/Host.java @@ -6,11 +6,11 @@ import java.util.Set; -import javax.persistence.CascadeType; -import javax.persistence.Entity; -import javax.persistence.FetchType; -import javax.persistence.Id; -import javax.persistence.OneToMany; +import jakarta.persistence.CascadeType; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.Id; +import jakarta.persistence.OneToMany; @Entity @Getter diff --git a/src/main/java/com/gw/jpa/LogActivity.java b/src/main/java/com/gw/jpa/LogActivity.java index bbf5ed82f..77131ddec 100644 --- a/src/main/java/com/gw/jpa/LogActivity.java +++ b/src/main/java/com/gw/jpa/LogActivity.java @@ -2,8 +2,8 @@ import lombok.Data; -import javax.persistence.Entity; -import javax.persistence.Id; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; @Entity @Data diff --git a/src/main/java/com/gw/jpa/Workflow.java b/src/main/java/com/gw/jpa/Workflow.java index e81e688f3..03f71b947 100644 --- a/src/main/java/com/gw/jpa/Workflow.java +++ b/src/main/java/com/gw/jpa/Workflow.java @@ -2,9 +2,9 @@ import lombok.Data; -import javax.persistence.Lob; -import javax.persistence.Entity; -import javax.persistence.Id; +import jakarta.persistence.Lob; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; @Entity @Data diff --git a/src/main/java/com/gw/local/LocalSessionOutput.java b/src/main/java/com/gw/local/LocalSessionOutput.java index 33ab90f6c..805f225e9 100644 --- a/src/main/java/com/gw/local/LocalSessionOutput.java +++ b/src/main/java/com/gw/local/LocalSessionOutput.java @@ -8,7 +8,7 @@ import com.gw.utils.BaseTool; import com.gw.utils.ProcessStatusCache; import java.io.BufferedReader; -import javax.websocket.Session; +import jakarta.websocket.Session; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; diff --git a/src/main/java/com/gw/server/CommandServlet.java b/src/main/java/com/gw/server/CommandServlet.java index 2512b608a..8b9479464 100644 --- a/src/main/java/com/gw/server/CommandServlet.java +++ b/src/main/java/com/gw/server/CommandServlet.java @@ -8,13 +8,13 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import javax.websocket.EndpointConfig; -import javax.websocket.OnClose; -import javax.websocket.OnError; -import javax.websocket.OnMessage; -import javax.websocket.OnOpen; -import javax.websocket.Session; -import javax.websocket.server.ServerEndpoint; +import jakarta.websocket.EndpointConfig; +import jakarta.websocket.OnClose; +import jakarta.websocket.OnError; +import jakarta.websocket.OnMessage; +import jakarta.websocket.OnOpen; +import jakarta.websocket.Session; +import jakarta.websocket.server.ServerEndpoint; import org.apache.tomcat.websocket.WsSession; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -268,13 +268,13 @@ public void close(final Session session) { * @param token The token used to identify the WebSocket session. * @return The WebSocket session, or null if not found. */ - public static javax.websocket.Session findSessionById(String token) { + public static jakarta.websocket.Session findSessionById(String token) { if (token == null || token.isEmpty()) { logger.warn("Cannot find session with null or empty token"); return null; } - javax.websocket.Session se = null; + jakarta.websocket.Session se = null; if (peers.containsKey(token)) { se = peers.get(token); if (se == null) { diff --git a/src/main/java/com/gw/server/FileUploadServlet.java b/src/main/java/com/gw/server/FileUploadServlet.java index 96be582b0..e2c9f15ec 100644 --- a/src/main/java/com/gw/server/FileUploadServlet.java +++ b/src/main/java/com/gw/server/FileUploadServlet.java @@ -3,36 +3,34 @@ import com.gw.utils.BaseTool; import java.io.File; import java.io.IOException; +import java.io.InputStream; import java.io.PrintWriter; -import java.util.Iterator; -import java.util.List; -import javax.servlet.ServletConfig; -import javax.servlet.ServletException; -import javax.servlet.annotation.WebServlet; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import org.apache.commons.fileupload.FileItem; -import org.apache.commons.fileupload.disk.DiskFileItemFactory; -import org.apache.commons.fileupload.servlet.ServletFileUpload; +import java.nio.file.Files; +import java.nio.file.StandardCopyOption; +import jakarta.servlet.ServletConfig; +import jakarta.servlet.ServletException; +import jakarta.servlet.annotation.MultipartConfig; +import jakarta.servlet.annotation.WebServlet; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.servlet.http.Part; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; -/** The servlet for uploading a file */ +/** The servlet for uploading a file (Jakarta Servlet multipart API). */ @WebServlet( name = "FileUploadServlet", urlPatterns = {"/FileUploadServlet"}) +@MultipartConfig(fileSizeThreshold = 1024 * 1024, maxFileSize = 2000L * 1024 * 1024, maxRequestSize = 2000L * 1024 * 1024) public class FileUploadServlet extends HttpServlet { private String relativePath = null, filePath = null, tempPath = null, - prefix_url = null, - callback = null; - - private int maxvol = 2000; + prefix_url = null; Logger logger = LoggerFactory.getLogger(getClass()); @@ -49,81 +47,60 @@ public class FileUploadServlet extends HttpServlet { @Override public void init(ServletConfig config) throws ServletException { - - // super.init(config); - - // relativePath = config.getInitParameter("filepath"); - // - // tempPath = config.getInitParameter("temppath"); - relativePath = upload_file_path; - tempPath = temp_file_path; - - // filePath = bt.getWebAppRootPath() + relativePath; - // - // tempPath = bt.getWebAppRootPath() + tempPath; - filePath = bt.getFileTransferFolder(); - tempPath = bt.normalizedPath(workspace) + "/" + tempPath; File uploadfolder = new File(filePath); - File tempfolder = new File(tempPath); - if (!uploadfolder.exists()) { - uploadfolder.mkdirs(); } - if (!tempfolder.exists()) { - tempfolder.mkdirs(); } } - /** - * Processes requests for both HTTP GET and POST methods. - * - * @param request servlet request - * @param response servlet response - * @throws ServletException if a servlet-specific error occurs - * @throws IOException if an I/O error occurs - */ protected void processRequest(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { res.setContentType("text/html; charset=utf-8"); PrintWriter pw = res.getWriter(); try { - // initialize the prefix url if (prefix_url == null) { - prefix_url = "download/" + relativePath + "/"; } - DiskFileItemFactory diskFactory = new DiskFileItemFactory(); - // threshold 4M - // extend to 2GB - updated by ziheng - 7/5/2018 - diskFactory.setSizeThreshold(maxvol * 1024); - // repository - logger.debug("Temp file path: " + tempPath); - File newrepo = new File(tempPath); - diskFactory.setRepository(newrepo); - - ServletFileUpload upload = new ServletFileUpload(diskFactory); - // 2000M - // upload.setSizeMax(maxvol * 1024 * 1024); - // HTTP - List fileItems = upload.parseRequest(req); - Iterator iter = fileItems.iterator(); - while (iter.hasNext()) { - FileItem item = (FileItem) iter.next(); - if (item.isFormField()) { - processFormField(item, pw); - } else { - processUploadFile(item, pw); + for (Part part : req.getParts()) { + String name = part.getName(); + String submitted = part.getSubmittedFileName(); + if (submitted == null || submitted.isEmpty()) { + if ("script".equals(name)) { + String value = new String(part.getInputStream().readAllBytes()); + logger.debug(name + " : " + value); + pw.println(""); + } + continue; } + + String filename = submitted; + int index = filename.lastIndexOf('\\'); + filename = filename.substring(index + 1); + if (filename.isEmpty() && part.getSize() == 0) { + throw new RuntimeException("You didn't upload a file."); + } + + File uploadFile = new File(filePath + "/" + filename); + try (InputStream in = part.getInputStream()) { + Files.copy(in, uploadFile.toPath(), StandardCopyOption.REPLACE_EXISTING); + } + + logger.debug("prefix url : " + prefix_url); + logger.info("file name : " + filename); + pw.print("{ \"url\": \"" + prefix_url + filename + "\", \"filename\": \"" + filename + "\" }"); + logger.debug(part.getSize() + "\r\n"); } } catch (Exception e) { e.printStackTrace(); @@ -134,99 +111,20 @@ protected void processRequest(HttpServletRequest req, HttpServletResponse res) } } - /** - * Information of the fields except file fields - * - * @param item - * @param pw - * @throws Exception - */ - private void processFormField(FileItem item, PrintWriter pw) throws Exception { - String name = item.getFieldName(); - String value = item.getString(); - logger.debug(name + " : " + value + "\r\n"); - if (name.equals("script")) { - pw.println(""); - } - // pw.println(name + " : " + value + "\r\n"); - } - - /** - * Process uploaded file - * - * @param item - * @param pw - * @throws Exception - */ - private void processUploadFile(FileItem item, PrintWriter pw) throws Exception { - String filename = item.getName(); - - int index = filename.lastIndexOf("\\"); - - filename = filename.substring(index + 1, filename.length()); - - long fileSize = item.getSize(); - - if ("".equals(filename) && fileSize == 0) { - throw new RuntimeException("You didn't upload a file."); - // return; - } - - File uploadFile = new File(filePath + "/" + filename); - - item.write(uploadFile); - - logger.debug("prefix url : " + prefix_url); - - logger.info("file name : " + filename); - - // pw.println("Link to the - // uploaded file : "+filename); - pw.print("{ \"url\": \"" + prefix_url + filename + "\", \"filename\": \"" + filename + "\" }"); - - logger.debug(fileSize + "\r\n"); - } - - /** - * Handles the HTTP GET method. - * - * @param request servlet request - * @param response servlet response - * @throws ServletException if a servlet-specific error occurs - * @throws IOException if an I/O error occurs - */ @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { - PrintWriter pw = response.getWriter(); - pw.println("wrong way"); - pw.flush(); } - /** - * Handles the HTTP POST method. - * - * @param request servlet request - * @param response servlet response - * @throws ServletException if a servlet-specific error occurs - * @throws IOException if an I/O error occurs - */ @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { processRequest(request, response); } - /** - * Returns a short description of the servlet. - * - * @return a String containing servlet description - */ @Override public String getServletInfo() { return "Short description"; diff --git a/src/main/java/com/gw/server/TerminalServlet.java b/src/main/java/com/gw/server/TerminalServlet.java index 02dc3bc15..f5a8ee563 100644 --- a/src/main/java/com/gw/server/TerminalServlet.java +++ b/src/main/java/com/gw/server/TerminalServlet.java @@ -6,13 +6,13 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import javax.websocket.EndpointConfig; -import javax.websocket.OnClose; -import javax.websocket.OnError; -import javax.websocket.OnMessage; -import javax.websocket.OnOpen; -import javax.websocket.Session; -import javax.websocket.server.ServerEndpoint; +import jakarta.websocket.EndpointConfig; +import jakarta.websocket.OnClose; +import jakarta.websocket.OnError; +import jakarta.websocket.OnMessage; +import jakarta.websocket.OnOpen; +import jakarta.websocket.Session; +import jakarta.websocket.server.ServerEndpoint; import org.apache.tomcat.websocket.WsSession; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -174,7 +174,7 @@ public void close(final Session session) { } } - public static javax.websocket.Session findSessionById(String sessionid) { + public static jakarta.websocket.Session findSessionById(String sessionid) { if (peers.containsKey(sessionid)) { return peers.get(sessionid); } diff --git a/src/main/java/com/gw/server/Test2SocketServlet.java b/src/main/java/com/gw/server/Test2SocketServlet.java index 150e2543f..366c80f57 100644 --- a/src/main/java/com/gw/server/Test2SocketServlet.java +++ b/src/main/java/com/gw/server/Test2SocketServlet.java @@ -1,13 +1,13 @@ package com.gw.server; -import javax.websocket.EndpointConfig; -import javax.websocket.OnClose; -import javax.websocket.OnError; -import javax.websocket.OnMessage; -import javax.websocket.OnOpen; -import javax.websocket.Session; -import javax.websocket.server.PathParam; -import javax.websocket.server.ServerEndpoint; +import jakarta.websocket.EndpointConfig; +import jakarta.websocket.OnClose; +import jakarta.websocket.OnError; +import jakarta.websocket.OnMessage; +import jakarta.websocket.OnOpen; +import jakarta.websocket.Session; +import jakarta.websocket.server.PathParam; +import jakarta.websocket.server.ServerEndpoint; @ServerEndpoint(value = "/test-socket2/{hostid}/api/kernels/{uuid1}/channels") public class Test2SocketServlet { diff --git a/src/main/java/com/gw/server/TestSocketServlet.java b/src/main/java/com/gw/server/TestSocketServlet.java index 00064302a..677547fd5 100644 --- a/src/main/java/com/gw/server/TestSocketServlet.java +++ b/src/main/java/com/gw/server/TestSocketServlet.java @@ -4,13 +4,13 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import javax.websocket.EndpointConfig; -import javax.websocket.OnClose; -import javax.websocket.OnError; -import javax.websocket.OnMessage; -import javax.websocket.OnOpen; -import javax.websocket.Session; -import javax.websocket.server.ServerEndpoint; +import jakarta.websocket.EndpointConfig; +import jakarta.websocket.OnClose; +import jakarta.websocket.OnError; +import jakarta.websocket.OnMessage; +import jakarta.websocket.OnOpen; +import jakarta.websocket.Session; +import jakarta.websocket.server.ServerEndpoint; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/src/main/java/com/gw/server/WebsocketMessageHandler.java b/src/main/java/com/gw/server/WebsocketMessageHandler.java index 3ba7d9c0c..85fe3c52c 100644 --- a/src/main/java/com/gw/server/WebsocketMessageHandler.java +++ b/src/main/java/com/gw/server/WebsocketMessageHandler.java @@ -1,6 +1,6 @@ package com.gw.server; -import javax.websocket.MessageHandler; +import jakarta.websocket.MessageHandler; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -12,7 +12,7 @@ */ public class WebsocketMessageHandler implements MessageHandler.Whole { - javax.websocket.Session jssession = null; + jakarta.websocket.Session jssession = null; String pairid; diff --git a/src/main/java/com/gw/server/WorkflowServlet.java b/src/main/java/com/gw/server/WorkflowServlet.java index 14f441ed7..684530842 100644 --- a/src/main/java/com/gw/server/WorkflowServlet.java +++ b/src/main/java/com/gw/server/WorkflowServlet.java @@ -3,13 +3,13 @@ import java.io.IOException; import java.util.HashMap; import java.util.Map; -import javax.websocket.EndpointConfig; -import javax.websocket.OnClose; -import javax.websocket.OnError; -import javax.websocket.OnMessage; -import javax.websocket.OnOpen; -import javax.websocket.Session; -import javax.websocket.server.ServerEndpoint; +import jakarta.websocket.EndpointConfig; +import jakarta.websocket.OnClose; +import jakarta.websocket.OnError; +import jakarta.websocket.OnMessage; +import jakarta.websocket.OnOpen; +import jakarta.websocket.Session; +import jakarta.websocket.server.ServerEndpoint; import org.apache.log4j.Logger; import org.apache.tomcat.websocket.WsSession; import com.gw.utils.BaseTool; @@ -145,8 +145,8 @@ public void close(final Session session) { * @param token The token used to identify the WebSocket session. * @return The WebSocket session, or null if not found. */ - public static javax.websocket.Session findSessionByToken(String token) { - javax.websocket.Session se = null; + public static jakarta.websocket.Session findSessionByToken(String token) { + jakarta.websocket.Session se = null; if (token != null && peers.containsKey(token)) { se = peers.get(token); } diff --git a/src/main/java/com/gw/ssh/SSHCmdSessionOutput.java b/src/main/java/com/gw/ssh/SSHCmdSessionOutput.java index 5fb2ad4a7..5d25f3a6c 100644 --- a/src/main/java/com/gw/ssh/SSHCmdSessionOutput.java +++ b/src/main/java/com/gw/ssh/SSHCmdSessionOutput.java @@ -7,7 +7,7 @@ import com.gw.utils.BaseTool; import com.gw.utils.ProcessStatusCache; import java.io.BufferedReader; -import javax.websocket.Session; +import jakarta.websocket.Session; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; diff --git a/src/main/java/com/gw/ssh/SSHLiveSessionOutput.java b/src/main/java/com/gw/ssh/SSHLiveSessionOutput.java index a9c060219..ea71ea794 100644 --- a/src/main/java/com/gw/ssh/SSHLiveSessionOutput.java +++ b/src/main/java/com/gw/ssh/SSHLiveSessionOutput.java @@ -27,7 +27,7 @@ this software and associated documentation files (the "Software"), to deal in import com.gw.server.TerminalServlet; import com.gw.utils.BaseTool; import java.io.BufferedReader; -import javax.websocket.Session; +import jakarta.websocket.Session; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; diff --git a/src/main/java/com/gw/ssh/SecurityConfiguration.java b/src/main/java/com/gw/ssh/SecurityConfiguration.java index 80ca20fda..aca37aebc 100644 --- a/src/main/java/com/gw/ssh/SecurityConfiguration.java +++ b/src/main/java/com/gw/ssh/SecurityConfiguration.java @@ -4,66 +4,51 @@ import org.apache.log4j.Logger; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.config.annotation.web.builders.WebSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; -import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; +import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.web.SecurityFilterChain; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.CorsConfigurationSource; import org.springframework.web.cors.UrlBasedCorsConfigurationSource; -// import com.gw.tools.SessionManager; - @Configuration @EnableWebSecurity -public class SecurityConfiguration extends WebSecurityConfigurerAdapter { +public class SecurityConfiguration { protected final Logger log = Logger.getLogger(getClass()); - @Bean - public SSHAuthenticationProvider sshAuthentication() { - SSHAuthenticationProvider sshAuthentication = new SSHAuthenticationProvider(); - return sshAuthentication; + private final SSHAuthenticationProvider sshAuthenticationProvider; + + public SecurityConfiguration(SSHAuthenticationProvider sshAuthenticationProvider) { + this.sshAuthenticationProvider = sshAuthenticationProvider; } - /* - * TODO - use 'registerAuthentication' in 3.2.0.RC1 and 'configure' in 3.2.0.RELEASE - * but note that 'configure' does not appear to work in tomcat7 - */ - @Override - protected void configure(AuthenticationManagerBuilder auth) throws Exception { + @Bean + public AuthenticationManager authenticationManager(HttpSecurity http) throws Exception { log.info("registering SSH authentication provider"); - auth.authenticationProvider(sshAuthentication()); + AuthenticationManagerBuilder authenticationManagerBuilder = + http.getSharedObject(AuthenticationManagerBuilder.class); + authenticationManagerBuilder.authenticationProvider(sshAuthenticationProvider); + return authenticationManagerBuilder.build(); } - /** - * WebSecurity is used to ask Spring Security to bypass the following resources WebSecurity is - * based on HttpSecurity - */ - @Override - public void configure(WebSecurity builder) throws Exception {} - - @Override - protected void configure(HttpSecurity http) throws Exception { - - http.authorizeRequests() - .antMatchers("/Geoweaver/**") - .permitAll() - .and() - .formLogin() - .loginProcessingUrl("/Geoweaver/users/login") - .and() - .logout() - .and(); - - http.cors().and().csrf().disable(); - - http.headers().frameOptions().disable(); + @Bean + public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { + http.authorizeHttpRequests( + auth -> auth.requestMatchers("/Geoweaver/**").permitAll().anyRequest().permitAll()) + .formLogin(form -> form.loginProcessingUrl("/Geoweaver/users/login")) + .logout(logout -> {}) + .cors(cors -> {}) + .csrf(AbstractHttpConfigurer::disable) + .headers(HeadersConfigurer::disable); - http.headers().disable(); // this must be turned off to make the JupyterHub work + return http.build(); } @Bean diff --git a/src/main/java/com/gw/tasks/GeoweaverProcessTask.java b/src/main/java/com/gw/tasks/GeoweaverProcessTask.java index b16ce9967..bd8fc035f 100644 --- a/src/main/java/com/gw/tasks/GeoweaverProcessTask.java +++ b/src/main/java/com/gw/tasks/GeoweaverProcessTask.java @@ -16,7 +16,7 @@ import java.util.List; import java.util.Optional; -import javax.websocket.Session; +import jakarta.websocket.Session; import org.apache.log4j.Logger; import org.json.simple.JSONArray; import org.json.simple.JSONObject; @@ -99,10 +99,10 @@ public class GeoweaverProcessTask extends Task { Logger logger = Logger.getLogger(this.getClass()); /** This monitor is used to return the logs while process is running */ - javax.websocket.Session monitor = null; + jakarta.websocket.Session monitor = null; /** This monitor is used to return the status of the process in a running workflow */ - javax.websocket.Session workflow_monitor = null; + jakarta.websocket.Session workflow_monitor = null; public GeoweaverProcessTask() { @@ -326,7 +326,7 @@ public String getHistory_id() { * * @param socketsession */ - public void startMonitor(javax.websocket.Session socketsession) { + public void startMonitor(jakarta.websocket.Session socketsession) { monitor = socketsession; } diff --git a/src/main/java/com/gw/tasks/GeoweaverWorkflowTask.java b/src/main/java/com/gw/tasks/GeoweaverWorkflowTask.java index a4fd472c3..467599698 100644 --- a/src/main/java/com/gw/tasks/GeoweaverWorkflowTask.java +++ b/src/main/java/com/gw/tasks/GeoweaverWorkflowTask.java @@ -21,7 +21,7 @@ import java.util.Map; import java.util.Optional; -import javax.websocket.Session; +import jakarta.websocket.Session; import org.apache.log4j.Logger; import org.json.simple.JSONArray; import org.json.simple.JSONObject; diff --git a/src/main/java/com/gw/tasks/Task.java b/src/main/java/com/gw/tasks/Task.java index f550c2427..457b754f9 100644 --- a/src/main/java/com/gw/tasks/Task.java +++ b/src/main/java/com/gw/tasks/Task.java @@ -1,6 +1,6 @@ package com.gw.tasks; -import javax.websocket.Session; +import jakarta.websocket.Session; /** * Class Task.java diff --git a/src/main/java/com/gw/tools/BuiltinTool.java b/src/main/java/com/gw/tools/BuiltinTool.java index f2632cba5..e6d760be2 100644 --- a/src/main/java/com/gw/tools/BuiltinTool.java +++ b/src/main/java/com/gw/tools/BuiltinTool.java @@ -6,7 +6,7 @@ import com.gw.utils.BaseTool; import java.io.File; import java.io.IOException; -import javax.websocket.Session; +import jakarta.websocket.Session; import org.apache.log4j.Logger; import org.json.simple.JSONArray; import org.json.simple.JSONObject; diff --git a/src/main/java/com/gw/tools/SessionPair.java b/src/main/java/com/gw/tools/SessionPair.java index 6bbd2526e..21e1caa21 100644 --- a/src/main/java/com/gw/tools/SessionPair.java +++ b/src/main/java/com/gw/tools/SessionPair.java @@ -2,7 +2,7 @@ import java.util.Iterator; import java.util.Set; -import javax.websocket.Session; +import jakarta.websocket.Session; public class SessionPair { diff --git a/src/main/java/com/gw/tools/UserTool.java b/src/main/java/com/gw/tools/UserTool.java index 727af904d..652899de6 100644 --- a/src/main/java/com/gw/tools/UserTool.java +++ b/src/main/java/com/gw/tools/UserTool.java @@ -14,7 +14,7 @@ import java.util.Map; import java.util.Optional; import java.util.Set; -import javax.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletRequest; import org.apache.log4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Scope; diff --git a/src/main/java/com/gw/utils/BaseTool.java b/src/main/java/com/gw/utils/BaseTool.java index ce985f545..6b12bc4c6 100644 --- a/src/main/java/com/gw/utils/BaseTool.java +++ b/src/main/java/com/gw/utils/BaseTool.java @@ -44,7 +44,7 @@ import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; import java.util.zip.ZipOutputStream; -import javax.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletRequest; import org.apache.commons.text.StringEscapeUtils; import org.dom4j.Document; import org.dom4j.io.SAXReader; diff --git a/src/main/java/com/gw/utils/HttpUtil.java b/src/main/java/com/gw/utils/HttpUtil.java index 2f5cddff0..9e7843b92 100644 --- a/src/main/java/com/gw/utils/HttpUtil.java +++ b/src/main/java/com/gw/utils/HttpUtil.java @@ -1,6 +1,6 @@ package com.gw.utils; -import javax.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletRequest; public class HttpUtil { diff --git a/src/main/java/com/gw/web/FileDownloadController.java b/src/main/java/com/gw/web/FileDownloadController.java index ee3a789c4..dbc4f059e 100644 --- a/src/main/java/com/gw/web/FileDownloadController.java +++ b/src/main/java/com/gw/web/FileDownloadController.java @@ -6,8 +6,8 @@ import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import org.apache.log4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; diff --git a/src/main/java/com/gw/web/GeoweaverController.java b/src/main/java/com/gw/web/GeoweaverController.java index 8164de60f..9c5c2d912 100644 --- a/src/main/java/com/gw/web/GeoweaverController.java +++ b/src/main/java/com/gw/web/GeoweaverController.java @@ -29,11 +29,11 @@ import java.io.InputStream; import java.util.*; import java.util.concurrent.ConcurrentHashMap; -import javax.annotation.PreDestroy; -import javax.servlet.http.Cookie; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import javax.servlet.http.HttpSession; +import jakarta.annotation.PreDestroy; +import jakarta.servlet.http.Cookie; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpSession; import com.gw.database.CheckpointRepository; import com.gw.database.WorkflowRepository; diff --git a/src/main/java/com/gw/web/GoogleEarthController.java b/src/main/java/com/gw/web/GoogleEarthController.java index f4cf19a6b..52416724e 100644 --- a/src/main/java/com/gw/web/GoogleEarthController.java +++ b/src/main/java/com/gw/web/GoogleEarthController.java @@ -9,17 +9,10 @@ import java.net.URLDecoder; import java.util.ArrayList; import java.util.List; -import javax.servlet.http.HttpServletRequest; -import org.apache.http.client.config.CookieSpecs; -import org.apache.http.client.config.RequestConfig; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClients; +import jakarta.servlet.http.HttpServletRequest; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.web.client.RestTemplateBuilder; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Scope; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; @@ -27,7 +20,7 @@ import org.springframework.http.MediaType; import org.springframework.http.RequestEntity; import org.springframework.http.ResponseEntity; -import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; +import org.springframework.http.client.SimpleClientHttpRequestFactory; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; @@ -48,7 +41,7 @@ public class GoogleEarthController { private String scheme = "https"; - @Autowired RestTemplate restTemplate; + private RestTemplate restTemplate; HttpHeaders headers = new HttpHeaders(); @@ -60,34 +53,19 @@ public class GoogleEarthController { int TIMEOUT = 30000; - public GoogleEarthController(RestTemplateBuilder builder) {} - - @Bean(name = "GErestTemplate") - @Scope("prototype") - public RestTemplate getRestTemplate() { - - RestTemplate GErestTemplate = new RestTemplate(); - - HttpComponentsClientHttpRequestFactory requestFactory = - new HttpComponentsClientHttpRequestFactory(); - - requestFactory.setConnectTimeout(TIMEOUT); - requestFactory.setReadTimeout(TIMEOUT); - - CloseableHttpClient httpClient = - HttpClients.custom() - .setDefaultRequestConfig( - RequestConfig.custom() - .setCookieSpec(CookieSpecs.STANDARD) - .setRedirectsEnabled(true) - .build()) - .build(); - - GErestTemplate.setRequestFactory(requestFactory); - - logger.debug("A new Google Earth restTemplate is created"); - - return GErestTemplate; + public GoogleEarthController() {} + + private RestTemplate getRestTemplate() { + if (restTemplate == null) { + RestTemplate template = new RestTemplate(); + SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory(); + requestFactory.setConnectTimeout(TIMEOUT); + requestFactory.setReadTimeout(TIMEOUT); + template.setRequestFactory(requestFactory); + restTemplate = template; + logger.debug("A new Google Earth restTemplate is created"); + } + return restTemplate; } public String getRealTargetURL(String referurl) { @@ -539,7 +517,7 @@ private ResponseEntity processGET( // logger.debug(newentity.toString()); ResponseEntity responseEntity = - restTemplate.exchange(targeturl, HttpMethod.GET, newentity, byte[].class); + getRestTemplate().exchange(targeturl, HttpMethod.GET, newentity, byte[].class); String contenttype = getHeaderProperty(responseEntity.getHeaders(), "Content-Type"); @@ -566,7 +544,7 @@ private ResponseEntity processGET( resp = new ResponseEntity(newbody, headers, responseEntity.getStatusCode()); - restTemplate.exchange( + getRestTemplate().exchange( "https://code.earthengine.google.com/", HttpMethod.GET, newentity, byte[].class); logger.debug("response entity: " + responseEntity.toString()); @@ -792,7 +770,7 @@ private ResponseEntity processPost( String target_url = getRealTargetURL(newheaders.get("target_url").get(0)); ResponseEntity responseEntity = - restTemplate.exchange(target_url, method, newentity, String.class); + getRestTemplate().exchange(target_url, method, newentity, String.class); String newbody = addURLProxy(responseEntity.getBody(), hostid); diff --git a/src/main/java/com/gw/web/LoginInterceptor.java b/src/main/java/com/gw/web/LoginInterceptor.java index af9668a06..af3f46430 100644 --- a/src/main/java/com/gw/web/LoginInterceptor.java +++ b/src/main/java/com/gw/web/LoginInterceptor.java @@ -1,10 +1,10 @@ package com.gw.web; import com.gw.utils.BaseTool; -import javax.servlet.http.Cookie; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import javax.servlet.http.HttpSession; +import jakarta.servlet.http.Cookie; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpSession; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; diff --git a/src/main/java/com/gw/web/PortalController.java b/src/main/java/com/gw/web/PortalController.java index d6038d3df..5105ebecd 100644 --- a/src/main/java/com/gw/web/PortalController.java +++ b/src/main/java/com/gw/web/PortalController.java @@ -1,7 +1,7 @@ package com.gw.web; import com.gw.utils.BaseTool; -import javax.servlet.http.HttpSession; +import jakarta.servlet.http.HttpSession; import org.apache.log4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; diff --git a/src/main/java/com/gw/web/UserController.java b/src/main/java/com/gw/web/UserController.java index 089d7118f..acf7b5e58 100644 --- a/src/main/java/com/gw/web/UserController.java +++ b/src/main/java/com/gw/web/UserController.java @@ -8,8 +8,8 @@ import com.gw.utils.RandomString; import java.util.Date; import java.util.Optional; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpSession; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpSession; import org.apache.log4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; diff --git a/src/main/java/com/gw/workers/WorkerManager.java b/src/main/java/com/gw/workers/WorkerManager.java index 19fc19199..9e1ca431b 100644 --- a/src/main/java/com/gw/workers/WorkerManager.java +++ b/src/main/java/com/gw/workers/WorkerManager.java @@ -7,6 +7,7 @@ import org.apache.log4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Service; @@ -22,7 +23,7 @@ public class WorkerManager { private List workerlist; - @Autowired TaskManager tm; + @Autowired @Lazy TaskManager tm; @Value("${geoweaver.workernumber}") String worknumber2; diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 3c8166336..9bc20e3a0 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -33,6 +33,10 @@ spring.h2.console.path=/h2_console # for production, this option should be disabled spring.h2.console.settings.web-allow-others=true +# OpenAPI (springdoc) — replaces Springfox +springdoc.api-docs.path=/v3/api-docs +springdoc.swagger-ui.path=/swagger-ui.html + #### properties to enable debugging # spring.jpa.show-sql=true diff --git a/src/main/resources/templates/reset_password.html b/src/main/resources/templates/reset_password.html index 866abdf9e..801e89246 100644 --- a/src/main/resources/templates/reset_password.html +++ b/src/main/resources/templates/reset_password.html @@ -1,6 +1,6 @@ + xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity6"> diff --git a/src/test/java/com/gw/AbstractHelperMethodsTest.java b/src/test/java/com/gw/AbstractHelperMethodsTest.java index afd556719..73936fba0 100644 --- a/src/test/java/com/gw/AbstractHelperMethodsTest.java +++ b/src/test/java/com/gw/AbstractHelperMethodsTest.java @@ -33,7 +33,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.web.client.TestRestTemplate; -import org.springframework.boot.web.server.LocalServerPort; +import org.springframework.boot.test.web.server.LocalServerPort; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; @@ -43,7 +43,7 @@ import org.apache.log4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.web.client.TestRestTemplate; -import org.springframework.boot.web.server.LocalServerPort; +import org.springframework.boot.test.web.server.LocalServerPort; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; diff --git a/src/test/java/com/gw/BaseToolTest.java b/src/test/java/com/gw/BaseToolTest.java index e64277730..c11136313 100644 --- a/src/test/java/com/gw/BaseToolTest.java +++ b/src/test/java/com/gw/BaseToolTest.java @@ -19,7 +19,7 @@ import org.mockito.InjectMocks; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.web.server.LocalServerPort; +import org.springframework.boot.test.web.server.LocalServerPort; import org.springframework.mock.web.MockHttpServletRequest; @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = GeoweaverApplication.class) diff --git a/src/test/java/com/gw/EnvironmentTest.java b/src/test/java/com/gw/EnvironmentTest.java index 8d0403582..46c9a8d0f 100644 --- a/src/test/java/com/gw/EnvironmentTest.java +++ b/src/test/java/com/gw/EnvironmentTest.java @@ -23,7 +23,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.web.client.TestRestTemplate; -import org.springframework.boot.web.server.LocalServerPort; +import org.springframework.boot.test.web.server.LocalServerPort; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; diff --git a/src/test/java/com/gw/FileTest.java b/src/test/java/com/gw/FileTest.java index b40255dbc..67fc7a9ff 100644 --- a/src/test/java/com/gw/FileTest.java +++ b/src/test/java/com/gw/FileTest.java @@ -28,7 +28,7 @@ import org.springframework.boot.info.BuildProperties; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.web.client.TestRestTemplate; -import org.springframework.boot.web.server.LocalServerPort; +import org.springframework.boot.test.web.server.LocalServerPort; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; diff --git a/src/test/java/com/gw/GeneralTests.java b/src/test/java/com/gw/GeneralTests.java index ef340455d..3667bead4 100644 --- a/src/test/java/com/gw/GeneralTests.java +++ b/src/test/java/com/gw/GeneralTests.java @@ -24,7 +24,7 @@ import org.springframework.boot.info.BuildProperties; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.web.client.TestRestTemplate; -import org.springframework.boot.web.server.LocalServerPort; +import org.springframework.boot.test.web.server.LocalServerPort; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; diff --git a/src/test/java/com/gw/GeoweaverControllerTest.java b/src/test/java/com/gw/GeoweaverControllerTest.java index d24d82b39..dc0e90aa6 100644 --- a/src/test/java/com/gw/GeoweaverControllerTest.java +++ b/src/test/java/com/gw/GeoweaverControllerTest.java @@ -24,7 +24,7 @@ import org.springframework.boot.info.BuildProperties; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.web.client.TestRestTemplate; -import org.springframework.boot.web.server.LocalServerPort; +import org.springframework.boot.test.web.server.LocalServerPort; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; diff --git a/src/test/java/com/gw/GoogleEarthEngineTest.java b/src/test/java/com/gw/GoogleEarthEngineTest.java index e24eda258..1b4a87a10 100644 --- a/src/test/java/com/gw/GoogleEarthEngineTest.java +++ b/src/test/java/com/gw/GoogleEarthEngineTest.java @@ -24,7 +24,7 @@ import org.springframework.boot.info.BuildProperties; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.web.client.TestRestTemplate; -import org.springframework.boot.web.server.LocalServerPort; +import org.springframework.boot.test.web.server.LocalServerPort; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; diff --git a/src/test/java/com/gw/HistoryTest.java b/src/test/java/com/gw/HistoryTest.java index 357487144..e6e7cd7fb 100644 --- a/src/test/java/com/gw/HistoryTest.java +++ b/src/test/java/com/gw/HistoryTest.java @@ -38,7 +38,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.web.client.TestRestTemplate; -import org.springframework.boot.web.server.LocalServerPort; +import org.springframework.boot.test.web.server.LocalServerPort; import org.springframework.context.annotation.Import; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; diff --git a/src/test/java/com/gw/HostGeneralTest.java b/src/test/java/com/gw/HostGeneralTest.java index 7dba79e8a..0ed244612 100644 --- a/src/test/java/com/gw/HostGeneralTest.java +++ b/src/test/java/com/gw/HostGeneralTest.java @@ -16,7 +16,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.web.client.TestRestTemplate; -import org.springframework.boot.web.server.LocalServerPort; +import org.springframework.boot.test.web.server.LocalServerPort; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; diff --git a/src/test/java/com/gw/LocalSessionTests.java b/src/test/java/com/gw/LocalSessionTests.java index 2013bc85d..7f3a4fa62 100644 --- a/src/test/java/com/gw/LocalSessionTests.java +++ b/src/test/java/com/gw/LocalSessionTests.java @@ -8,11 +8,11 @@ import java.net.URI; import java.util.Map; -import javax.websocket.CloseReason; -import javax.websocket.Session; -import javax.websocket.HandshakeResponse; -import javax.websocket.server.HandshakeRequest; -import javax.websocket.server.ServerEndpointConfig; +import jakarta.websocket.CloseReason; +import jakarta.websocket.Session; +import jakarta.websocket.HandshakeResponse; +import jakarta.websocket.server.HandshakeRequest; +import jakarta.websocket.server.ServerEndpointConfig; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonMappingException; @@ -34,7 +34,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.web.client.TestRestTemplate; -import org.springframework.boot.web.server.LocalServerPort; +import org.springframework.boot.test.web.server.LocalServerPort; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; diff --git a/src/test/java/com/gw/OtherFunctionalTest.java b/src/test/java/com/gw/OtherFunctionalTest.java index c29608503..55ed02592 100644 --- a/src/test/java/com/gw/OtherFunctionalTest.java +++ b/src/test/java/com/gw/OtherFunctionalTest.java @@ -23,7 +23,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.web.client.TestRestTemplate; -import org.springframework.boot.web.server.LocalServerPort; +import org.springframework.boot.test.web.server.LocalServerPort; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; diff --git a/src/test/java/com/gw/ProcessBuiltinTest.java b/src/test/java/com/gw/ProcessBuiltinTest.java index 03681a496..627aa9f2b 100644 --- a/src/test/java/com/gw/ProcessBuiltinTest.java +++ b/src/test/java/com/gw/ProcessBuiltinTest.java @@ -17,7 +17,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.web.client.TestRestTemplate; -import org.springframework.boot.web.server.LocalServerPort; +import org.springframework.boot.test.web.server.LocalServerPort; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; diff --git a/src/test/java/com/gw/ProcessGeneralTest.java b/src/test/java/com/gw/ProcessGeneralTest.java index fb2112e25..331e1f18c 100644 --- a/src/test/java/com/gw/ProcessGeneralTest.java +++ b/src/test/java/com/gw/ProcessGeneralTest.java @@ -25,7 +25,7 @@ import org.springframework.boot.info.BuildProperties; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.web.client.TestRestTemplate; -import org.springframework.boot.web.server.LocalServerPort; +import org.springframework.boot.test.web.server.LocalServerPort; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; diff --git a/src/test/java/com/gw/ProcessJupyterTest.java b/src/test/java/com/gw/ProcessJupyterTest.java index 162c8b442..cd5cc7150 100644 --- a/src/test/java/com/gw/ProcessJupyterTest.java +++ b/src/test/java/com/gw/ProcessJupyterTest.java @@ -17,7 +17,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.web.client.TestRestTemplate; -import org.springframework.boot.web.server.LocalServerPort; +import org.springframework.boot.test.web.server.LocalServerPort; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; diff --git a/src/test/java/com/gw/ProcessPythonTest.java b/src/test/java/com/gw/ProcessPythonTest.java index 0909fa494..485598a0e 100644 --- a/src/test/java/com/gw/ProcessPythonTest.java +++ b/src/test/java/com/gw/ProcessPythonTest.java @@ -31,7 +31,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.web.client.TestRestTemplate; -import org.springframework.boot.web.server.LocalServerPort; +import org.springframework.boot.test.web.server.LocalServerPort; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; diff --git a/src/test/java/com/gw/ProcessShellTest.java b/src/test/java/com/gw/ProcessShellTest.java index 9f1152183..5fc0f1846 100644 --- a/src/test/java/com/gw/ProcessShellTest.java +++ b/src/test/java/com/gw/ProcessShellTest.java @@ -16,7 +16,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.web.client.TestRestTemplate; -import org.springframework.boot.web.server.LocalServerPort; +import org.springframework.boot.test.web.server.LocalServerPort; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; diff --git a/src/test/java/com/gw/SSHSessionTest.java b/src/test/java/com/gw/SSHSessionTest.java index a020304aa..e3fae5255 100644 --- a/src/test/java/com/gw/SSHSessionTest.java +++ b/src/test/java/com/gw/SSHSessionTest.java @@ -21,7 +21,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.web.client.TestRestTemplate; -import org.springframework.boot.web.server.LocalServerPort; +import org.springframework.boot.test.web.server.LocalServerPort; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; diff --git a/src/test/java/com/gw/ServletTest.java b/src/test/java/com/gw/ServletTest.java index a3cb4a96d..a4aa65a40 100644 --- a/src/test/java/com/gw/ServletTest.java +++ b/src/test/java/com/gw/ServletTest.java @@ -7,10 +7,10 @@ import java.io.PrintWriter; import java.util.Map; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import javax.websocket.EndpointConfig; -import javax.websocket.Session; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.websocket.EndpointConfig; +import jakarta.websocket.Session; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonMappingException; @@ -35,7 +35,7 @@ import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.boot.test.web.client.TestRestTemplate; -import org.springframework.boot.web.server.LocalServerPort; +import org.springframework.boot.test.web.server.LocalServerPort; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; diff --git a/src/test/java/com/gw/UserTest.java b/src/test/java/com/gw/UserTest.java index 2ab401aab..c4c72a2ab 100644 --- a/src/test/java/com/gw/UserTest.java +++ b/src/test/java/com/gw/UserTest.java @@ -28,7 +28,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.web.client.TestRestTemplate; -import org.springframework.boot.web.server.LocalServerPort; +import org.springframework.boot.test.web.server.LocalServerPort; import org.springframework.context.annotation.Import; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; diff --git a/src/test/java/com/gw/UtilsTest.java b/src/test/java/com/gw/UtilsTest.java index 7d0198fec..54ed07686 100644 --- a/src/test/java/com/gw/UtilsTest.java +++ b/src/test/java/com/gw/UtilsTest.java @@ -14,7 +14,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.web.client.TestRestTemplate; -import org.springframework.boot.web.server.LocalServerPort; +import org.springframework.boot.test.web.server.LocalServerPort; @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = GeoweaverApplication.class) diff --git a/src/test/java/com/gw/WebsocketTest.java b/src/test/java/com/gw/WebsocketTest.java index 625792a8c..57592de5b 100644 --- a/src/test/java/com/gw/WebsocketTest.java +++ b/src/test/java/com/gw/WebsocketTest.java @@ -11,7 +11,7 @@ import java.util.concurrent.LinkedBlockingDeque; import java.util.concurrent.TimeUnit; -import javax.websocket.Session; +import jakarta.websocket.Session; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonMappingException; @@ -36,7 +36,7 @@ import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.boot.test.web.client.TestRestTemplate; -import org.springframework.boot.web.server.LocalServerPort; +import org.springframework.boot.test.web.server.LocalServerPort; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; diff --git a/src/test/java/com/gw/WorkflowTest.java b/src/test/java/com/gw/WorkflowTest.java index 322fa92a1..819922e85 100644 --- a/src/test/java/com/gw/WorkflowTest.java +++ b/src/test/java/com/gw/WorkflowTest.java @@ -44,7 +44,7 @@ import org.springframework.boot.info.BuildProperties; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.web.client.TestRestTemplate; -import org.springframework.boot.web.server.LocalServerPort; +import org.springframework.boot.test.web.server.LocalServerPort; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; diff --git a/src/test/java/com/gw/jpa/JPAAnnotationsTest.java b/src/test/java/com/gw/jpa/JPAAnnotationsTest.java index 3b5051438..6a1950a02 100644 --- a/src/test/java/com/gw/jpa/JPAAnnotationsTest.java +++ b/src/test/java/com/gw/jpa/JPAAnnotationsTest.java @@ -3,7 +3,7 @@ import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; -import javax.persistence.*; +import jakarta.persistence.*; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.Date; @@ -182,15 +182,16 @@ void testSpecialAnnotations() throws Exception { assertTrue(idField.isAnnotationPresent(GeneratedValue.class)); GeneratedValue generatedValue = idField.getAnnotation(GeneratedValue.class); - assertEquals("uuid2", generatedValue.generator()); - - // 测试Checkpoint的Type注解 - assertTrue(idField.isAnnotationPresent(org.hibernate.annotations.Type.class)); - + assertNotNull(generatedValue); + + // Hibernate 6 / Boot 3: UUID mapped with @JdbcTypeCode(CHAR) + assertTrue(idField.isAnnotationPresent(org.hibernate.annotations.JdbcTypeCode.class)); + assertTrue(idField.isAnnotationPresent(org.hibernate.annotations.UuidGenerator.class)); + // 测试Checkpoint的Column注解 assertTrue(idField.isAnnotationPresent(Column.class)); Column column = idField.getAnnotation(Column.class); - assertEquals("VARCHAR(36)", column.columnDefinition()); + assertEquals(36, column.length()); assertEquals("id", column.name()); // 测试Checkpoint的Table注解 diff --git a/src/test/java/com/gw/server/CommandServletTest.java b/src/test/java/com/gw/server/CommandServletTest.java index e3f8bb3c7..3e2d40809 100644 --- a/src/test/java/com/gw/server/CommandServletTest.java +++ b/src/test/java/com/gw/server/CommandServletTest.java @@ -8,9 +8,9 @@ import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; -import javax.websocket.EndpointConfig; -import javax.websocket.RemoteEndpoint; -import javax.websocket.Session; +import jakarta.websocket.EndpointConfig; +import jakarta.websocket.RemoteEndpoint; +import jakarta.websocket.Session; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; diff --git a/src/test/java/com/gw/server/FileUploadServletTest.java b/src/test/java/com/gw/server/FileUploadServletTest.java index e18b13f57..4ffc13ce4 100644 --- a/src/test/java/com/gw/server/FileUploadServletTest.java +++ b/src/test/java/com/gw/server/FileUploadServletTest.java @@ -1,30 +1,22 @@ package com.gw.server; import com.gw.utils.BaseTool; -import org.apache.commons.fileupload.FileItem; -import org.apache.commons.fileupload.disk.DiskFileItemFactory; -import org.apache.commons.fileupload.servlet.ServletFileUpload; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; -import org.mockito.MockedStatic; import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.test.util.ReflectionTestUtils; -import javax.servlet.ServletConfig; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.File; +import jakarta.servlet.ServletConfig; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; import java.io.StringWriter; -import java.util.ArrayList; -import java.util.List; import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.*; @ExtendWith(MockitoExtension.class) @@ -45,12 +37,6 @@ class FileUploadServletTest { @Mock private BaseTool baseTool; - @Mock - private FileItem fileItem; - - @Mock - private FileItem formFieldItem; - private FileUploadServlet fileUploadServlet; private StringWriter stringWriter; @@ -58,36 +44,28 @@ class FileUploadServletTest { void setUp() throws IOException { fileUploadServlet = new FileUploadServlet(); stringWriter = new StringWriter(); - - // Mock BaseTool + ReflectionTestUtils.setField(fileUploadServlet, "bt", baseTool); ReflectionTestUtils.setField(fileUploadServlet, "upload_file_path", "/test/upload"); ReflectionTestUtils.setField(fileUploadServlet, "temp_file_path", "/test/temp"); ReflectionTestUtils.setField(fileUploadServlet, "workspace", "/test/workspace"); - } @Test void testInit() throws ServletException { - // When & Then (test method exists and can be called) - // Note: This test may fail due to complex dependencies, but it verifies the method exists try { fileUploadServlet.init(servletConfig); } catch (Exception e) { - // Expected due to missing dependencies in test environment assertTrue(e instanceof NullPointerException || e instanceof RuntimeException); } } @Test void testDoGet() throws ServletException, IOException { - // Given when(response.getWriter()).thenReturn(printWriter); - - // When + fileUploadServlet.doGet(request, response); - // Then verify(response).getWriter(); verify(printWriter).println("wrong way"); verify(printWriter).flush(); @@ -95,70 +73,16 @@ void testDoGet() throws ServletException, IOException { @Test void testDoPost() throws ServletException, IOException { - // When & Then (test method exists and can be called) - // Note: This test may fail due to complex dependencies, but it verifies the method exists try { fileUploadServlet.doPost(request, response); } catch (Exception e) { - // Expected due to missing dependencies in test environment - assertTrue(e instanceof NullPointerException || e instanceof RuntimeException); - } - } - - @Test - void testProcessRequestWithFormField() throws Exception { - // When & Then (test method exists and can be called) - // Note: This test may fail due to complex dependencies, but it verifies the method exists - try { - fileUploadServlet.processRequest(request, response); - } catch (Exception e) { - // Expected due to missing dependencies in test environment - assertTrue(e instanceof NullPointerException || e instanceof RuntimeException); - } - } - - @Test - void testProcessRequestWithFileUpload() throws Exception { - // When & Then (test method exists and can be called) - // Note: This test may fail due to complex dependencies, but it verifies the method exists - try { - fileUploadServlet.processRequest(request, response); - } catch (Exception e) { - // Expected due to missing dependencies in test environment - assertTrue(e instanceof NullPointerException || e instanceof RuntimeException); - } - } - - @Test - void testProcessRequestWithEmptyFile() throws Exception { - // When & Then (test method exists and can be called) - // Note: This test may fail due to complex dependencies, but it verifies the method exists - try { - fileUploadServlet.processRequest(request, response); - } catch (Exception e) { - // Expected due to missing dependencies in test environment - assertTrue(e instanceof NullPointerException || e instanceof RuntimeException); - } - } - - @Test - void testProcessRequestWithException() throws Exception { - // When & Then (test method exists and can be called) - // Note: This test may fail due to complex dependencies, but it verifies the method exists - try { - fileUploadServlet.processRequest(request, response); - } catch (Exception e) { - // Expected due to missing dependencies in test environment assertTrue(e instanceof NullPointerException || e instanceof RuntimeException); } } @Test void testGetServletInfo() { - // When String info = fileUploadServlet.getServletInfo(); - - // Then assertEquals("Short description", info); } -} \ No newline at end of file +} diff --git a/src/test/java/com/gw/server/TerminalServletTest.java b/src/test/java/com/gw/server/TerminalServletTest.java index f4f62af88..43fa78f05 100644 --- a/src/test/java/com/gw/server/TerminalServletTest.java +++ b/src/test/java/com/gw/server/TerminalServletTest.java @@ -8,9 +8,9 @@ import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; -import javax.websocket.EndpointConfig; -import javax.websocket.RemoteEndpoint; -import javax.websocket.Session; +import jakarta.websocket.EndpointConfig; +import jakarta.websocket.RemoteEndpoint; +import jakarta.websocket.Session; import java.io.ByteArrayOutputStream; import java.io.IOException; diff --git a/src/test/java/com/gw/server/WorkflowServletTest.java b/src/test/java/com/gw/server/WorkflowServletTest.java index 542c24346..d0bb12b4d 100644 --- a/src/test/java/com/gw/server/WorkflowServletTest.java +++ b/src/test/java/com/gw/server/WorkflowServletTest.java @@ -8,9 +8,9 @@ import org.mockito.MockedStatic; import org.mockito.junit.jupiter.MockitoExtension; -import javax.websocket.EndpointConfig; -import javax.websocket.RemoteEndpoint; -import javax.websocket.Session; +import jakarta.websocket.EndpointConfig; +import jakarta.websocket.RemoteEndpoint; +import jakarta.websocket.Session; import java.io.IOException; import static org.junit.jupiter.api.Assertions.*; diff --git a/src/test/java/com/gw/tools/UserToolTest.java b/src/test/java/com/gw/tools/UserToolTest.java index bee6ecef2..d66bb1447 100644 --- a/src/test/java/com/gw/tools/UserToolTest.java +++ b/src/test/java/com/gw/tools/UserToolTest.java @@ -14,7 +14,7 @@ import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; -import javax.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletRequest; import java.util.*; import static org.junit.jupiter.api.Assertions.*; diff --git a/src/test/java/com/gw/utils/HttpUtilTest.java b/src/test/java/com/gw/utils/HttpUtilTest.java index a2ebc9be2..9c74e7440 100644 --- a/src/test/java/com/gw/utils/HttpUtilTest.java +++ b/src/test/java/com/gw/utils/HttpUtilTest.java @@ -6,7 +6,7 @@ import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; -import javax.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletRequest; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.*; diff --git a/src/test/java/com/gw/web/AsyncExportControllerTest.java b/src/test/java/com/gw/web/AsyncExportControllerTest.java index 21a45b653..a0c5f56d3 100644 --- a/src/test/java/com/gw/web/AsyncExportControllerTest.java +++ b/src/test/java/com/gw/web/AsyncExportControllerTest.java @@ -13,8 +13,8 @@ import org.springframework.ui.ModelMap; import org.springframework.web.context.request.WebRequest; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpSession; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpSession; import java.util.concurrent.ConcurrentHashMap; import static org.junit.jupiter.api.Assertions.*; From 729dafda3b8a591018d7663849b74f4fec3ab29e Mon Sep 17 00:00:00 2001 From: Ziheng Sun Date: Wed, 26 Aug 2026 23:45:06 -0400 Subject: [PATCH 02/10] Fix ServletTest compile after removing commons-fileupload. Drop unused FileItem import that broke Cypress CI testCompile, and document Java 17+ as the new minimum. Co-authored-by: Cursor --- README.md | 4 ++-- src/test/java/com/gw/ServletTest.java | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index b6f86fcf2..af20a2548 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ https://img.shields.io/github/issues/ESIPFed/Geoweaver?style=for-the-badge&color https://img.shields.io/codecov/c/github/ESIPFed/Geoweaver?style=for-the-badge )](https://codecov.io/) ![PyPi](https://img.shields.io/pypi/v/pygeoweaver?style=for-the-badge) -![Minimum Java Version](https://img.shields.io/badge/Java-11%2B-%23ed8b02?style=for-the-badge&logo=openjdk +![Minimum Java Version](https://img.shields.io/badge/Java-17%2B-%23ed8b02?style=for-the-badge&logo=openjdk ) ![Geoweaver Docs](https://img.shields.io/badge/Docs-Geoweaver-%23c4ff7d?style=for-the-badge&logo=readthedocs&link=https%3A%2F%2Fgeoweaver.dev%2F ) @@ -94,7 +94,7 @@ For detailed information, visit [Geoweaver Documentation](https://geoweaver.dev) ### Prerequisites -- ☕ Java 11 or higher (OpenJDK 11 or higher) +- ☕ Java 17 or higher (OpenJDK 17 or higher; Spring Boot 3 requires Java 17+) - 🐳 Docker (optional, for Docker installation) ### Quick Start diff --git a/src/test/java/com/gw/ServletTest.java b/src/test/java/com/gw/ServletTest.java index a4aa65a40..e405f9f43 100644 --- a/src/test/java/com/gw/ServletTest.java +++ b/src/test/java/com/gw/ServletTest.java @@ -23,7 +23,6 @@ import com.gw.tools.UserTool; import com.gw.utils.BaseTool; -import org.apache.commons.fileupload.FileItem; import org.apache.log4j.Logger; import static org.junit.jupiter.api.Assertions.*; import org.junit.jupiter.api.DisplayName; From d490a3924867a8503c10423170b5c762d1abfe63 Mon Sep 17 00:00:00 2001 From: Ziheng Sun Date: Wed, 26 Aug 2026 23:48:56 -0400 Subject: [PATCH 03/10] Warn and exit on JDK < 17; document legacy Geoweaver 2.1.x path. Latest Geoweaver requires Java 17+. Print a clear console warning and refuse to start on older JDKs, pointing users who cannot upgrade to Geoweaver 2.1.x releases. Co-authored-by: Cursor --- README.md | 3 +- docs/install.md | 6 +- .../java/com/gw/GeoweaverApplication.java | 5 +- src/main/java/com/gw/GeoweaverCLI.java | 1 + .../com/gw/utils/JavaRuntimeRequirements.java | 63 +++++++++++++++++++ 5 files changed, 75 insertions(+), 3 deletions(-) create mode 100644 src/main/java/com/gw/utils/JavaRuntimeRequirements.java diff --git a/README.md b/README.md index af20a2548..7e44548f4 100644 --- a/README.md +++ b/README.md @@ -94,7 +94,8 @@ For detailed information, visit [Geoweaver Documentation](https://geoweaver.dev) ### Prerequisites -- ☕ Java 17 or higher (OpenJDK 17 or higher; Spring Boot 3 requires Java 17+) +- ☕ Java **17 or higher** (OpenJDK 17+). Latest Geoweaver (2.2+ / Spring Boot 3) **does not support JDK < 17**. +- If you cannot upgrade Java, use an older Geoweaver release (**2.1.x**, Java 11): [Releases](https://github.com/ESIPFed/Geoweaver/releases) (example: [v2.1.7 jar](https://github.com/ESIPFed/Geoweaver/releases/download/v2.1.7/geoweaver.jar)). - 🐳 Docker (optional, for Docker installation) ### Quick Start diff --git a/docs/install.md b/docs/install.md index 4b13690b5..dc74aee63 100644 --- a/docs/install.md +++ b/docs/install.md @@ -3,7 +3,11 @@ ## Dependencies -Java 1.8+ (OpenJDK 8 or higher) +Java **17+** (OpenJDK 17 or higher). Latest Geoweaver (2.2+ / Spring Boot 3) **no longer supports JDK < 17**. + +If you cannot bump your JDK, stay on **Geoweaver 2.1.x** (Java 11): +see [GitHub Releases](https://github.com/ESIPFed/Geoweaver/releases) +(example jar: [v2.1.7](https://github.com/ESIPFed/Geoweaver/releases/download/v2.1.7/geoweaver.jar)). [Docker](https://docs.docker.com/install/) 18.09.1+ (only for installation via docker) diff --git a/src/main/java/com/gw/GeoweaverApplication.java b/src/main/java/com/gw/GeoweaverApplication.java index 6042915ed..dcc51bd4f 100644 --- a/src/main/java/com/gw/GeoweaverApplication.java +++ b/src/main/java/com/gw/GeoweaverApplication.java @@ -6,6 +6,7 @@ import com.gw.tools.UserTool; import com.gw.utils.BaseTool; import com.gw.utils.BeanTool; +import com.gw.utils.JavaRuntimeRequirements; import com.gw.utils.RandomString; import java.awt.Desktop; import java.io.File; @@ -30,7 +31,9 @@ public class GeoweaverApplication { public static void main(String[] args) { // Create log directory before anything else to avoid NoSuchFileException createLogDirectory(); - + // Latest Geoweaver requires Java 17+ (Spring Boot 3); refuse older JDKs early. + JavaRuntimeRequirements.requireSupportedJavaOrExit(); + // if we have a command line argument, we assume it is a command if (args.length > 0) { diff --git a/src/main/java/com/gw/GeoweaverCLI.java b/src/main/java/com/gw/GeoweaverCLI.java index 8812845b1..9d5547f37 100644 --- a/src/main/java/com/gw/GeoweaverCLI.java +++ b/src/main/java/com/gw/GeoweaverCLI.java @@ -55,6 +55,7 @@ public void run(String... args) { } public static void main(String[] args) throws Exception { + com.gw.utils.JavaRuntimeRequirements.requireSupportedJavaOrExit(); System.exit( SpringApplication.exit( diff --git a/src/main/java/com/gw/utils/JavaRuntimeRequirements.java b/src/main/java/com/gw/utils/JavaRuntimeRequirements.java new file mode 100644 index 000000000..18a58cbb8 --- /dev/null +++ b/src/main/java/com/gw/utils/JavaRuntimeRequirements.java @@ -0,0 +1,63 @@ +package com.gw.utils; + +/** + * Runtime Java version guard for Geoweaver 2.2+ (Spring Boot 3). + * + *

Java 17 is the minimum supported runtime. Users who cannot upgrade should stay on Geoweaver + * 2.1.x, which still runs on Java 11. + */ +public final class JavaRuntimeRequirements { + + public static final int MIN_MAJOR_VERSION = 17; + + /** Last major line that supported Java 11 / Spring Boot 2. */ + public static final String LEGACY_JAVA11_LINE = "Geoweaver 2.1.x"; + + public static final String RELEASES_URL = "https://github.com/ESIPFed/Geoweaver/releases"; + + public static final String LEGACY_JAR_EXAMPLE = + "https://github.com/ESIPFed/Geoweaver/releases/download/v2.1.7/geoweaver.jar"; + + private JavaRuntimeRequirements() {} + + public static int detectedMajorVersion() { + return Runtime.version().feature(); + } + + public static boolean isSupported() { + return detectedMajorVersion() >= MIN_MAJOR_VERSION; + } + + /** Print a clear console warning for JDK < 17 and instruct how to stay on legacy releases. */ + public static void printUnsupportedJavaWarning(int major) { + System.err.println(); + System.err.println("========================================================================"); + System.err.println(" Geoweaver WARNING: Unsupported Java version"); + System.err.println("========================================================================"); + System.err.println(" Detected Java major version: " + major); + System.err.println( + " Latest Geoweaver (2.2+ / Spring Boot 3) requires Java " + + MIN_MAJOR_VERSION + + " or newer."); + System.err.println(" JDK versions older than " + MIN_MAJOR_VERSION + " are no longer supported."); + System.err.println(); + System.err.println(" If you cannot bump your JDK, use an older Geoweaver release instead:"); + System.err.println(" - Stay on " + LEGACY_JAVA11_LINE + " (Java 11 compatible)"); + System.err.println(" - Releases: " + RELEASES_URL); + System.err.println(" - Example jar: " + LEGACY_JAR_EXAMPLE); + System.err.println(); + System.err.println(" PyGeoWeaver users: pin an older Geoweaver jar / older pygeoweaver,"); + System.err.println(" or upgrade the system JDK to 17+ before running `gw start`."); + System.err.println("========================================================================"); + System.err.println(); + } + + /** Exit the process if the runtime is below the supported Java major version. */ + public static void requireSupportedJavaOrExit() { + int major = detectedMajorVersion(); + if (major < MIN_MAJOR_VERSION) { + printUnsupportedJavaWarning(major); + System.exit(1); + } + } +} From 6df80d0c25abf9f479801f9ced4eec9c8d9f7b2d Mon Sep 17 00:00:00 2001 From: Ziheng Sun Date: Thu, 27 Aug 2026 00:46:04 -0400 Subject: [PATCH 04/10] Fix Maven test failures for Spring Boot 3 / Hibernate 6 migration. Align history queries, CLOB mapping, dashboard date handling, and HTTP client setup so mvn clean install passes under Java 17. Co-authored-by: Cursor --- pom.xml | 4 ++ .../com/gw/database/HistoryRepository.java | 22 +++++-- src/main/java/com/gw/jpa/History.java | 9 ++- src/main/java/com/gw/tools/DashboardTool.java | 58 ++++++++++--------- src/main/java/com/gw/tools/ProcessTool.java | 31 ++++++++-- src/main/java/com/gw/tools/WorkflowTool.java | 2 +- .../com/gw/web/GoogleEarthController.java | 18 ++++-- .../com/gw/AbstractHelperMethodsTest.java | 14 ++++- src/test/java/com/gw/GeneralTests.java | 7 ++- src/test/java/com/gw/HostGeneralTest.java | 5 +- .../com/gw/tools/EnvironmentToolTest.java | 38 ++++++------ 11 files changed, 136 insertions(+), 72 deletions(-) diff --git a/pom.xml b/pom.xml index 31c8fa7b1..035efde0c 100644 --- a/pom.xml +++ b/pom.xml @@ -170,6 +170,10 @@ expectit-core 0.9.0 + + org.apache.httpcomponents.client5 + httpclient5 + org.apache.httpcomponents httpclient diff --git a/src/main/java/com/gw/database/HistoryRepository.java b/src/main/java/com/gw/database/HistoryRepository.java index 834900f49..42bd2f919 100644 --- a/src/main/java/com/gw/database/HistoryRepository.java +++ b/src/main/java/com/gw/database/HistoryRepository.java @@ -25,9 +25,10 @@ public interface HistoryRepository extends JpaRepository { * @param limit The maximum number of history records to retrieve. * @return A collection of recent history records for the host. */ - @Query(value = "SELECT history_id, history_begin_time, history_end_time, history_notes,"+ - " history_process, host_id, indicator FROM history WHERE host_id = ?1 "+ - " ORDER BY history_begin_time DESC LIMIT ?2", nativeQuery = true) + @Query( + value = + "SELECT * FROM history WHERE host_id = ?1 ORDER BY history_begin_time DESC LIMIT ?2", + nativeQuery = true) List findRecentHistory(String hostid, int limit); /** @@ -136,7 +137,12 @@ public interface HistoryRepository extends JpaRepository { * @param limit The maximum number of recent workflow records to retrieve. * @return A list of recent workflow records with additional information. */ - @Query(value = "SELECT * FROM history, workflow WHERE workflow.id = history.history_process ORDER BY history_begin_time DESC LIMIT ?1", nativeQuery = true) + @Query( + value = + "SELECT h.history_id, h.history_begin_time, h.history_end_time, w.name " + + "FROM history h JOIN workflow w ON w.id = h.history_process " + + "ORDER BY h.history_begin_time DESC LIMIT ?1", + nativeQuery = true) List findRecentWorkflow(int limit); @@ -146,7 +152,13 @@ public interface HistoryRepository extends JpaRepository { * @param limit The maximum number of recent process records to retrieve. * @return A list of recent process records with additional information. */ - @Query(value = "SELECT * FROM history, gwprocess WHERE gwprocess.id = history.history_process ORDER BY history_begin_time DESC LIMIT ?1", nativeQuery = true) + @Query( + value = + "SELECT h.history_id, h.history_begin_time, h.history_end_time, h.history_notes, " + + "h.indicator, p.name " + + "FROM history h JOIN gwprocess p ON p.id = h.history_process " + + "ORDER BY h.history_begin_time DESC LIMIT ?1", + nativeQuery = true) List findRecentProcess(int limit); diff --git a/src/main/java/com/gw/jpa/History.java b/src/main/java/com/gw/jpa/History.java index 193502d68..7b5bd6478 100644 --- a/src/main/java/com/gw/jpa/History.java +++ b/src/main/java/com/gw/jpa/History.java @@ -3,11 +3,11 @@ import lombok.Data; - -import jakarta.persistence.Column; -import jakarta.persistence.Lob; import jakarta.persistence.Entity; import jakarta.persistence.Id; +import jakarta.persistence.Lob; +import org.hibernate.annotations.JdbcTypeCode; +import org.hibernate.type.SqlTypes; import java.util.Date; @Entity @@ -18,9 +18,11 @@ public class History { private String history_id; @Lob + @JdbcTypeCode(SqlTypes.CLOB) private String history_input; @Lob + @JdbcTypeCode(SqlTypes.CLOB) private String history_output; private Date history_begin_time; @@ -28,6 +30,7 @@ public class History { private Date history_end_time; @Lob + @JdbcTypeCode(SqlTypes.CLOB) private String history_notes; private String history_process; diff --git a/src/main/java/com/gw/tools/DashboardTool.java b/src/main/java/com/gw/tools/DashboardTool.java index c52f4a138..b327b5b70 100644 --- a/src/main/java/com/gw/tools/DashboardTool.java +++ b/src/main/java/com/gw/tools/DashboardTool.java @@ -38,46 +38,48 @@ public int[] getAllProcessTimeCosts() { for (int i = 0; i < failed_processes.size(); i++) { - // System.out.println("========="); - Object[] cols = (Object[]) failed_processes.get(i); - if (cols[1] != null && cols[2] != null) { - - Date begin_time = bt.parseSQLDateStr(String.valueOf(cols[1])); - Date end_time = bt.parseSQLDateStr(String.valueOf(cols[2])); + costs[num++] = calculateTimeCostMillis(cols); + } - int diffInMillies = ((Long) Math.abs(end_time.getTime() - begin_time.getTime())).intValue(); + for (int i = 0; i < success_processes.size(); i++) { - costs[num++] = diffInMillies; - } else { + Object[] cols = (Object[]) success_processes.get(i); - costs[num++] = -1; - } + costs[num++] = calculateTimeCostMillis(cols); } - for (int i = 0; i < success_processes.size(); i++) { + return costs; + } - // System.out.println("========="); + private int calculateTimeCostMillis(Object[] cols) { - Object[] cols = (Object[]) success_processes.get(i); + if (cols[1] == null || cols[2] == null) { + return -1; + } - if (cols[1] != null && cols[2] != null) { + Date begin_time = toHistoryDate(cols[1]); + Date end_time = toHistoryDate(cols[2]); - Date begin_time = bt.parseSQLDateStr(String.valueOf(cols[1])); + if (begin_time == null || end_time == null) { + return -1; + } - Date end_time = bt.parseSQLDateStr(String.valueOf(cols[2])); + return (int) Math.abs(end_time.getTime() - begin_time.getTime()); + } - int diffInMillies = ((Long) Math.abs(end_time.getTime() - begin_time.getTime())).intValue(); + private Date toHistoryDate(Object value) { - costs[num++] = diffInMillies; - } else { + if (value instanceof Date dateValue) { + return dateValue; + } - costs[num++] = -1; - } + if (value instanceof java.sql.Timestamp timestamp) { + return new Date(timestamp.getTime()); } - return costs; + return bt.parseSQLDateStr(String.valueOf(value)); } public String getAllProcessTimeCostsJSON() { @@ -100,15 +102,15 @@ public String getAllProcessTimeCostsJSON() { public String getJSON() { - int process_num = ((Long) processrepository.count()).intValue(); + int process_num = (int) processrepository.count(); - int history_num = ((Long) historyrepository.count()).intValue(); + int history_num = (int) historyrepository.count(); - int host_num = ((Long) hostrepository.count()).intValue(); + int host_num = (int) hostrepository.count(); - int workflow_num = ((Long) workflowrepository.count()).intValue(); + int workflow_num = (int) workflowrepository.count(); - int environment_num = ((Long) environmentrepository.count()).intValue(); + int environment_num = (int) environmentrepository.count(); int process_shell_num = processrepository.findShellProcess().size(); diff --git a/src/main/java/com/gw/tools/ProcessTool.java b/src/main/java/com/gw/tools/ProcessTool.java index fab719cb7..e1a54fa99 100644 --- a/src/main/java/com/gw/tools/ProcessTool.java +++ b/src/main/java/com/gw/tools/ProcessTool.java @@ -1,5 +1,6 @@ package com.gw.tools; +import java.sql.Clob; import java.sql.SQLException; import java.util.ArrayList; import java.util.Iterator; @@ -541,17 +542,17 @@ public String recent(int limit) { Object[] process_obj = recent_processes.get(num); - resp.append("{ \"id\": \"").append(process_obj[0]).append("\", "); + resp.append("{ \"id\": \"").append(formatRecentField(process_obj[0])).append("\", "); - resp.append("\"name\": \"").append(process_obj[14]).append("\", "); + resp.append("\"name\": \"").append(formatRecentField(process_obj[5])).append("\", "); - resp.append("\"notes\": \"").append(process_obj[4]).append("\", "); + resp.append("\"notes\": \"").append(formatRecentField(process_obj[3])).append("\", "); - resp.append("\"end_time\": \"").append(process_obj[2]).append("\", "); + resp.append("\"end_time\": \"").append(formatRecentField(process_obj[2])).append("\", "); - resp.append("\"status\": \"").append(process_obj[8]).append("\", "); + resp.append("\"status\": \"").append(formatRecentField(process_obj[4])).append("\", "); - resp.append("\"begin_time\": \"").append(process_obj[1]).append("\"}"); + resp.append("\"begin_time\": \"").append(formatRecentField(process_obj[1])).append("\"}"); } @@ -566,6 +567,24 @@ public String recent(int limit) { return resp.toString(); } + + private String formatRecentField(Object value) { + if (value == null) { + return "null"; + } + if (value instanceof Clob clob) { + try { + long length = clob.length(); + if (length > Integer.MAX_VALUE) { + return clob.getSubString(1, Integer.MAX_VALUE); + } + return clob.getSubString(1, (int) length); + } catch (SQLException e) { + return String.valueOf(value); + } + } + return String.valueOf(value); + } public String removeClob(String clob) { diff --git a/src/main/java/com/gw/tools/WorkflowTool.java b/src/main/java/com/gw/tools/WorkflowTool.java index 673d419be..4cc7acc44 100644 --- a/src/main/java/com/gw/tools/WorkflowTool.java +++ b/src/main/java/com/gw/tools/WorkflowTool.java @@ -499,7 +499,7 @@ public String recent(int limit) { resp.append("{ \"id\": \"").append(recent_his[0]).append("\", "); // history id - resp.append("\"name\": \"").append(recent_his[13]).append("\", "); + resp.append("\"name\": \"").append(recent_his[3]).append("\", "); resp.append("\"end_time\": \"").append(recent_his[2]).append("\", "); diff --git a/src/main/java/com/gw/web/GoogleEarthController.java b/src/main/java/com/gw/web/GoogleEarthController.java index 52416724e..9ce587dc7 100644 --- a/src/main/java/com/gw/web/GoogleEarthController.java +++ b/src/main/java/com/gw/web/GoogleEarthController.java @@ -20,7 +20,11 @@ import org.springframework.http.MediaType; import org.springframework.http.RequestEntity; import org.springframework.http.ResponseEntity; -import org.springframework.http.client.SimpleClientHttpRequestFactory; +import org.apache.hc.client5.http.config.RequestConfig; +import org.apache.hc.client5.http.impl.classic.HttpClients; +import org.apache.hc.core5.util.Timeout; +import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; +import java.time.Duration; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; @@ -58,9 +62,15 @@ public GoogleEarthController() {} private RestTemplate getRestTemplate() { if (restTemplate == null) { RestTemplate template = new RestTemplate(); - SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory(); - requestFactory.setConnectTimeout(TIMEOUT); - requestFactory.setReadTimeout(TIMEOUT); + RequestConfig requestConfig = + RequestConfig.custom() + .setConnectionRequestTimeout(Timeout.ofMilliseconds(TIMEOUT)) + .setResponseTimeout(Timeout.ofMilliseconds(TIMEOUT)) + .build(); + HttpComponentsClientHttpRequestFactory requestFactory = + new HttpComponentsClientHttpRequestFactory( + HttpClients.custom().setDefaultRequestConfig(requestConfig).build()); + requestFactory.setConnectTimeout(Duration.ofMillis(TIMEOUT)); template.setRequestFactory(requestFactory); restTemplate = template; logger.debug("A new Google Earth restTemplate is created"); diff --git a/src/test/java/com/gw/AbstractHelperMethodsTest.java b/src/test/java/com/gw/AbstractHelperMethodsTest.java index 73936fba0..6c654fdbd 100644 --- a/src/test/java/com/gw/AbstractHelperMethodsTest.java +++ b/src/test/java/com/gw/AbstractHelperMethodsTest.java @@ -34,9 +34,9 @@ import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.web.client.TestRestTemplate; import org.springframework.boot.test.web.server.LocalServerPort; -import org.springframework.http.HttpEntity; -import org.springframework.http.HttpHeaders; -import org.springframework.http.MediaType; +import org.apache.hc.client5.http.impl.classic.HttpClients; +import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; +import org.springframework.web.client.RestTemplate; import net.bytebuddy.utility.RandomString; @@ -70,6 +70,14 @@ public abstract class AbstractHelperMethodsTest { Logger logger = Logger.getLogger(this.getClass()); + protected RestTemplate noRedirectRestTemplate() { + RestTemplate restTemplate = new RestTemplate(); + restTemplate.setRequestFactory( + new HttpComponentsClientHttpRequestFactory( + HttpClients.custom().disableRedirectHandling().build())); + return restTemplate; + } + // This method is kept here in this file due to 3 other files // (EnviromentTest.java, OtherFunctionalTests.java, and HostTest.java) // utilizing this method. Leaving this here will prevent code repetition diff --git a/src/test/java/com/gw/GeneralTests.java b/src/test/java/com/gw/GeneralTests.java index 3667bead4..3b0403719 100644 --- a/src/test/java/com/gw/GeneralTests.java +++ b/src/test/java/com/gw/GeneralTests.java @@ -29,6 +29,7 @@ import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; +import org.springframework.web.client.RestTemplate; @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = GeoweaverApplication.class) class GeneralTests extends AbstractHelperMethodsTest { @@ -465,11 +466,13 @@ void testErrorPage() { @Test void testPortalController() { - ResponseEntity getresult = this.testrestTemplate.getForEntity( + RestTemplate redirectTemplate = noRedirectRestTemplate(); + + ResponseEntity getresult = redirectTemplate.getForEntity( "http://localhost:" + this.port + "/Geoweaver/geoweaver", String.class); assertThat(getresult.getStatusCode().value()).isEqualTo(302); - getresult = this.testrestTemplate.getForEntity( + getresult = redirectTemplate.getForEntity( "http://localhost:" + this.port + "/Geoweaver/web", String.class); assertThat(getresult.getStatusCode().value()).isEqualTo(302); diff --git a/src/test/java/com/gw/HostGeneralTest.java b/src/test/java/com/gw/HostGeneralTest.java index 0ed244612..e63ea4691 100644 --- a/src/test/java/com/gw/HostGeneralTest.java +++ b/src/test/java/com/gw/HostGeneralTest.java @@ -21,6 +21,7 @@ import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; +import org.springframework.web.client.RestTemplate; @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = GeoweaverApplication.class) public class HostGeneralTest extends AbstractHelperMethodsTest { @@ -153,7 +154,9 @@ void testHostEdit() throws Exception{ @Test void testSSHLoginPageRedirect(){ - ResponseEntity result = this.testrestTemplate.getForEntity("http://localhost:" + this.port + "/Geoweaver/web/geoweaver-ssh?token=venustoken", + RestTemplate redirectTemplate = noRedirectRestTemplate(); + + ResponseEntity result = redirectTemplate.getForEntity("http://localhost:" + this.port + "/Geoweaver/web/geoweaver-ssh?token=venustoken", String.class); assertThat(result.getStatusCode().value()).isEqualTo(302); diff --git a/src/test/java/com/gw/tools/EnvironmentToolTest.java b/src/test/java/com/gw/tools/EnvironmentToolTest.java index 4a37dc579..f6cbf079b 100644 --- a/src/test/java/com/gw/tools/EnvironmentToolTest.java +++ b/src/test/java/com/gw/tools/EnvironmentToolTest.java @@ -24,12 +24,12 @@ @ExtendWith(MockitoExtension.class) public class EnvironmentToolTest { - @Mock - private EnvironmentRepository environmentRepository; - @Mock private EnvironmentRepository envrep; + @Mock + private EnvironmentRepository environmentrepository; + @Mock private BaseTool baseTool; @@ -121,7 +121,7 @@ void testAddNewEnvironmentWithNewEnvironment() { environmentTool.addNewEnvironment(pypath, oldEnvList, hostId, name); // Then - verify(environmentRepository).save(any(Environment.class)); + verify(environmentrepository).save(any(Environment.class)); } @Test @@ -143,7 +143,7 @@ void testAddNewEnvironmentWithConda() { environmentTool.addNewEnvironment(pypath, oldEnvList, hostId, name); // Then - verify(environmentRepository).save(any(Environment.class)); + verify(environmentrepository).save(any(Environment.class)); } @Test @@ -157,7 +157,7 @@ void testGetEnvironmentsByHostId() { environment.setName("Test Environment"); environments.add(environment); - when(environmentRepository.findEnvByHost(hostId)).thenReturn(environments); + when(environmentrepository.findEnvByHost(hostId)).thenReturn(environments); // When List result = environmentTool.getEnvironmentsByHostId(hostId); @@ -173,7 +173,7 @@ void testGetEnvironmentsByHostIdWithException() { // Given String hostId = "host123"; - when(environmentRepository.findEnvByHost(hostId)).thenThrow(new RuntimeException("Database error")); + when(environmentrepository.findEnvByHost(hostId)).thenThrow(new RuntimeException("Database error")); // When List result = environmentTool.getEnvironmentsByHostId(hostId); @@ -195,7 +195,7 @@ void testSaveEnvironment() { environmentTool.saveEnvironment(environment); // Then - verify(environmentRepository).save(environment); + verify(environmentrepository).save(environment); } @Test @@ -226,7 +226,7 @@ void testGetEnvironmentsWithException() { // Given String hostId = "host123"; - when(environmentRepository.findEnvByHost(hostId)).thenThrow(new RuntimeException("Database error")); + when(environmentrepository.findEnvByHost(hostId)).thenThrow(new RuntimeException("Database error")); // When String result = environmentTool.getEnvironments(hostId); @@ -285,20 +285,20 @@ void testShowAllEnvironment() { environment.setName("Test Environment"); environments.add(environment); - when(environmentRepository.findAll()).thenReturn(environments); + when(environmentrepository.findAll()).thenReturn(environments); // When environmentTool.showAllEnvironment(); // Then - verify(environmentRepository).findAll(); + verify(environmentrepository).findAll(); } @Test @Timeout(10) void testShowAllEnvironmentWithException() { // Given - when(environmentRepository.findAll()).thenThrow(new RuntimeException("Database error")); + when(environmentrepository.findAll()).thenThrow(new RuntimeException("Database error")); // When environmentTool.showAllEnvironment(); @@ -322,7 +322,7 @@ void testGetEnvironmentByBEB() { environment.setName("Test Environment"); environments.add(environment); - when(environmentRepository.findEnvByID_BIN_ENV_BaseDir(hostId, bin, env, basedir)).thenReturn(environments); + when(environmentrepository.findEnvByID_BIN_ENV_BaseDir(hostId, bin, env, basedir)).thenReturn(environments); // When String result = environmentTool.getEnvironmentByBEB(hostId, bin, env, basedir); @@ -342,7 +342,7 @@ void testGetEnvironmentByBEBWithException() { String env = "pip"; String basedir = "/tmp"; - when(environmentRepository.findEnvByID_BIN_ENV_BaseDir(hostId, bin, env, basedir)) + when(environmentrepository.findEnvByID_BIN_ENV_BaseDir(hostId, bin, env, basedir)) .thenThrow(new RuntimeException("Database error")); // When @@ -563,13 +563,13 @@ void testAddEnv() { // Mock static method calls when(hostRepository.findById(hostId)).thenReturn(Optional.of(host)); - when(environmentRepository.findEnvByID_BIN(hostId, bin)).thenReturn(new ArrayList<>()); + when(environmentrepository.findEnvByID_BIN(hostId, bin)).thenReturn(new ArrayList<>()); // When String result = environmentTool.addEnv(historyId, hostId, type, bin, env, basedir, settings); // Then - verify(environmentRepository).save(any(Environment.class)); + verify(environmentrepository).save(any(Environment.class)); } @Test @@ -593,7 +593,7 @@ void testAddEnvWithExistingEnvironment() { existingEnv.setBin(bin); // Mock static method calls - when(environmentRepository.findEnvByID_BIN(hostId, bin)).thenReturn(Arrays.asList(existingEnv)); + when(environmentrepository.findEnvByID_BIN(hostId, bin)).thenReturn(Arrays.asList(existingEnv)); // When String result = environmentTool.addEnv(historyId, hostId, type, bin, env, basedir, settings); @@ -601,7 +601,7 @@ void testAddEnvWithExistingEnvironment() { // Then // Should return null when environment already exists (as per implementation) assertNull(result); - verify(environmentRepository, never()).save(any(Environment.class)); + verify(environmentrepository, never()).save(any(Environment.class)); } @Test @@ -622,7 +622,7 @@ void testAddEnvWithNullValues() { // Then // Should return null when bin is null assertNull(result); - verify(environmentRepository, never()).save(any(Environment.class)); + verify(environmentrepository, never()).save(any(Environment.class)); } @Test From c73354c05d5c160b2767c31f7efbc9aa04b4f23c Mon Sep 17 00:00:00 2001 From: Ziheng Sun Date: Thu, 27 Aug 2026 00:53:31 -0400 Subject: [PATCH 05/10] Fix Cypress CI to run the login-aware spec and skip IntroJS safely. Point the workflow at e2e/spec.cy.js, add skipIntroIfPresent/visitGeoweaver helpers, and remove the stale nested Cypress copy that ignored localhost login. Co-authored-by: Cursor --- .github/workflows/cypress_tests.yml | 4 +- cypress/cypress.config.js | 7 +- cypress/cypress/e2e/spec.cy.js | 267 -------------------------- cypress/cypress/fixtures/example.json | 5 - cypress/cypress/support/commands.js | 25 --- cypress/cypress/support/e2e.js | 20 -- cypress/e2e/spec.cy.js | 96 ++------- cypress/support/commands.js | 17 +- 8 files changed, 37 insertions(+), 404 deletions(-) delete mode 100644 cypress/cypress/e2e/spec.cy.js delete mode 100644 cypress/cypress/fixtures/example.json delete mode 100644 cypress/cypress/support/commands.js delete mode 100644 cypress/cypress/support/e2e.js diff --git a/.github/workflows/cypress_tests.yml b/.github/workflows/cypress_tests.yml index 88d4455b1..f102109c1 100644 --- a/.github/workflows/cypress_tests.yml +++ b/.github/workflows/cypress_tests.yml @@ -42,8 +42,10 @@ jobs: uses: cypress-io/github-action@v6 with: browser: chrome - spec: cypress/e2e/spec.cy.js + spec: e2e/spec.cy.js working-directory: ./cypress + env: + localhost_password: '123456' - name: Archive screenshots on failure if: failure() uses: actions/upload-artifact@v4 diff --git a/cypress/cypress.config.js b/cypress/cypress.config.js index 8376fc4c6..f7db48fd6 100644 --- a/cypress/cypress.config.js +++ b/cypress/cypress.config.js @@ -6,7 +6,8 @@ module.exports = defineConfig({ codeCoverage: { url: 'http://localhost:3000/__coverage__' }, - home: os.homedir(), + home: os.homedir(), + localhost_password: '123456', }, e2e: { experimentalStudio: true, @@ -25,8 +26,8 @@ module.exports = defineConfig({ });} }, - screenshotsFolder: 'cypress/screenshots', - videosFolder: 'cypress/videos', + screenshotsFolder: 'screenshots', + videosFolder: 'videos', }) diff --git a/cypress/cypress/e2e/spec.cy.js b/cypress/cypress/e2e/spec.cy.js deleted file mode 100644 index 64adf44bf..000000000 --- a/cypress/cypress/e2e/spec.cy.js +++ /dev/null @@ -1,267 +0,0 @@ - -/* * Test suite to verify the operational status of the application at 'http://localhost:8070/Geoweaver'. */ -Cypress.on('uncaught:exception', (err, runnable) => { - // Returning false prevents Cypress from failing the test - console.error('Uncaught exception occurred:', err); - return false; -}); - -describe('Application Build Check', () => { - it('Application is up and running', () => { - cy.request('http://localhost:8070/Geoweaver') - .then((response) => { - // Assert the HTTP status code is 200 - expect(response.status).to.eq(200); - }); - }); - it('Navigating through intro', () => { - cy.visit('http://localhost:8070/Geoweaver/web/geoweaver'); - cy.get('.introjs-nextbutton').click(); - cy.get('.introjs-nextbutton').click(); - cy.get('.introjs-nextbutton').click(); - cy.get('.introjs-nextbutton').click(); - cy.get('.introjs-nextbutton').click(); - cy.get('#main-general-content').click(); - cy.get('.lead > b').should('be.visible'); - }) -}); - -describe('Navigation', () => { - it('should navigate to different pages', () => { - cy.visit('http://localhost:8070/Geoweaver'); - cy.get('.introjs-skipbutton').click(); - - cy.contains('Log' , { timeout: 10000 }).click(); - cy.contains('Logging').should('be.visible'); - - cy.contains('Status' , { timeout: 10000 }).click(); - cy.contains('Real Time Status').should('be.visible'); - - cy.contains('Guide' , { timeout: 10000 }).click(); - cy.contains('Geoweaver User Guide').should('be.visible'); - - cy.contains('Tools' , { timeout: 10000 }).click(); - cy.get('[data-intro="All the other tools"]').click(); - cy.get('[onclick="GW.feedback.showDialog()"]').click(); - - cy.get(':nth-child(5) > .btn').should('be.visible'); - - cy.contains('History' , { timeout: 10000 }).click(); - cy.contains('Recent History').should('be.visible'); - - cy.contains('Contact' , { timeout: 10000 }).click(); - cy.contains('Have feedback?').should('be.visible'); - - cy.contains('About' , { timeout: 10000 }).click(); - cy.contains('Geoweaver is a web system').should('be.visible'); - }); -}); - - -describe('Host Testing', () => { - it('should submit a create new host form successfully', () => { - cy.visit('http://localhost:8070/Geoweaver'); - cy.get('.introjs-skipbutton').click(); - cy.get('#newhost').click(); - - cy.get('#hostip').type('1.1.1.1'); - cy.get('#hostport').type('8000'); - cy.get('#username').type('newuser'); - - cy.get('#host-add-btn').click(); - cy.get('.nav-side-menu').contains('New Host') - }); - it('Search result should be successful after creating the host', () => { - /* ==== Generated with Cypress Studio ==== */ - cy.visit('http://localhost:8070/Geoweaver/web/geoweaver'); - cy.get('.introjs-skipbutton').click(); - cy.get('[data-intro="All the other tools"]').click(); - cy.get('#toolbar-search').click(); - cy.get('#keywords').clear('New Host'); - cy.get('#keywords').type('New Host'); - cy.get('#search').click(); - cy.get('tbody > :nth-child(1) > :nth-child(1) > a').click(); - cy.get('tbody > :nth-child(1) > :nth-child(1) > a').click(); - cy.get('tbody > :nth-child(1) > :nth-child(1) > a').should('be.visible'); - - cy.get( - '[style="position: absolute; top: -18px; left: 8px; width: 8px; height: 8px; cursor: pointer; margin: 0px; padding: 0px; box-sizing: content-box; font-family: sans-serif; text-align: center; font-size: 8px; line-height: 8px; border-width: 1px; border-radius: 5px; border-color: rgb(252, 97, 92); border-style: solid; background-color: rgb(252, 97, 92); color: white; z-index: 50; user-select: none;"]' - ).click(); - - cy.get( - '[style="position: absolute; top: -18px; left: 8px; width: 8px; height: 8px; cursor: pointer; margin: 0px; padding: 0px; box-sizing: content-box; font-family: sans-serif; text-align: center; font-size: 8px; line-height: 8px; border-width: 1px; border-radius: 5px; border-color: rgb(252, 97, 92); border-style: solid; background-color: rgb(252, 97, 92); color: white; z-index: 50; user-select: none;"]' - ).click(); - }) - - -// it('Global Search for Host', () => { -// cy.visit('http://localhost:8070/Geoweaver/web/geoweaver'); -// cy.get('.introjs-skipbutton').click(); -// cy.get('#instant_search_bar').clear('N'); -// cy.get('#instant_search_bar').type('New '); -// cy.get('#host-b4ijvz').should('be.visible'); -// }) -}); - -describe('Process Testing', () => { - - it('Create Python Process', () => { - cy.visit('http://localhost:8070/Geoweaver/web/geoweaver'); - cy.get('.introjs-skipbutton').click(); - cy.get('#newprocess').click(); - cy.get('form select.form-control.form-control-sm').select('Python'); - cy.get('form > :nth-child(1) > :nth-child(4)').clear('t'); - cy.get('form > :nth-child(1) > :nth-child(4)').type('python_test'); - cy.get('.modal-footer').contains('Add').click(); - cy.get('ul#process_folder_python_target').should('contain', 'python_test'); - }) - -}); - - -describe('Add Process to Weaver', () => { - it('Add to weaver - python', () => { - cy.visit('http://localhost:8070/Geoweaver/web/geoweaver'); - cy.get('.introjs-skipbutton').click(); - cy.get('#process_folder_python').click(); - cy.wait(3000); - cy.get('ul#process_folder_python_target').contains('button', 'Add to Weaver').click(); - cy.get('circle').should('be.visible'); - }) -}); - - - -describe('Delete Process', () => { - it('Delete Python Process', () => { - cy.visit('http://localhost:8070/Geoweaver/web/geoweaver'); - cy.get('.introjs-skipbutton').click().then( - () => { - cy.get('#process_folder_python > a', { timeout: 10000 }).should('be.visible'); - } - ) - cy.get('#process_folder_python').click(); - cy.wait(1000) - cy.get('ul#process_folder_python_target').contains('python_test').click(); - // Delete button now only has icon, no text - find by title or icon - cy.get('button[title="Delete Process"]').click(); - cy.get('#del-confirm-btn').click(); - cy.wait(1000); - cy.get('#main-general-content').click(); - cy.wait(1000); - cy.get('[style="color:rgb(38, 90, 139);text-align:center;font-family:\'lato\', sans-serif;font-size:80px"]').should('be.visible'); - }) - -}); - -describe('Create Python process and run it', () => { - it('creates python process and runs test', function() { - cy.visit('http://localhost:8070/Geoweaver/web/geoweaver'); - cy.get('.introjs-skipbutton').click(); - cy.get('#process_folder_python').click(); - cy.get('#newprocess').click(); - cy.get('[id^="processcategory-"]').select('python'); - cy.get('[id^="processname-"]').clear().type('check_this'); - cy.get('.view-lines').click(); - cy.get('[id^="add-process-"]').last().click(); - cy.get('[id^="process-"] > .row > .col-md-8 > span').last().click(); - cy.get('[onclick*="runProcess"]').last().click(); - cy.get('#host-execute-btn').click(); - cy.get('#process-confirm-btn').click(); - cy.get('#inputpswd').clear().type('123456'); - cy.get('#pswd-confirm-btn').click(); - cy.get('#process-log-window > :nth-child(3)').click(); - cy.get('#process-log-window > :nth-child(3) > span').should('be.visible'); - }); -}); - - - -describe('Hosts Testing', () => { - it('Create New Host', () => { - cy.visit('http://localhost:8070/Geoweaver/web/geoweaver'); - cy.get('.introjs-skipbutton').click(); - cy.get('#newhost').click(); - cy.get('#hostip').clear('1'); - cy.get('#hostip').type('1.1.1.1'); - cy.get('#hostport').clear('2'); - cy.get('#hostport').type('22'); - cy.get('#username').clear('n'); - cy.get('#username').type('newuser'); - cy.get('#host-add-btn').click(); - cy.get('ul#host_folder_ssh_target').should('contain', 'New Host'); - }) - - it('Delete New Host', () => { - cy.visit('http://localhost:8070/Geoweaver/web/geoweaver'); - cy.get('.introjs-skipbutton').click().then(() => { - // Ensure the window or form is fully loaded and active - cy.get('#host_folder_ssh > a', { timeout: 10000 }).should('be.visible'); // Wait until the form is visible - }); - cy.get('#host_folder_ssh > a').click(); - cy.get('ul#host_folder_ssh_target').contains('New Host').click(); - // Delete button now uses fa-trash icon instead of fa-minus, and has "Delete" text - cy.contains('button', 'Delete').click(); - cy.get('#del-confirm-btn').click(); - }) - - it('LocalHost Read Python Env', () => { - cy.visit('http://localhost:8070/Geoweaver/web/geoweaver'); - cy.get('.introjs-skipbutton').click().then(() => { - // Ensure the window or form is fully loaded and active - cy.get('#host_folder_ssh > a', { timeout: 10000 }).should('be.visible'); // Wait until the form is visible - }); - cy.get('#host_folder_ssh > a').click(); - cy.get('#host-100001').click(); - // Click on Python Env tab instead of .fab - cy.contains('Python Env').click(); - cy.get('#inputpswd').clear('1'); - cy.get('#inputpswd').type('123456'); - cy.get('#pswd-confirm-btn').click(); - cy.intercept('POST', '/Geoweaver/web/readEnvironment').as('readEnvironment'); - cy.wait('@readEnvironment').then((interception) => { - // Log the full response to the console - console.log('Read Environment Response:', interception.response.body); - - // Assert the HTTP status code is 200 - expect(interception.response.statusCode).to.equal(200); - - // Verify that the response body exists - expect(interception.response.body).to.exist; - - // You can add more specific assertions about the response content - // For example, if the response contains a success message or specific data structure - // expect(interception.response.body).to.have.property('success'); - }); - }) - - it('LocalHost File Upload', () => { - cy.visit('http://localhost:8070/Geoweaver/web/geoweaver'); - cy.get('.introjs-skipbutton').click().then(() => { - // Ensure the window or form is fully loaded and active - cy.get('#host_folder_ssh > a', { timeout: 10000 }).should('be.visible'); // Wait until the form is visible - }); - cy.get('#host_folder_ssh > a').click(); - cy.get('#host-100001').click(); - // Click on Upload tab instead of p > .fa-upload - cy.contains('Upload').click(); - - // Set up intercept BEFORE clicking the confirm button - cy.intercept('POST', '**/authenticateUser').as('authenticateUser'); - - cy.get('#inputpswd').clear('1'); - cy.get('#inputpswd').type('123456'); - cy.get('#pswd-confirm-btn').click(); - - // Wait for authenticateUser request (for localhost, this authenticates Geoweaver password) - cy.wait('@authenticateUser', { timeout: 10000 }).its('response.statusCode').should('eq', 200); - - // File uploader is now in upload-tab-pane, not host-file-uploader - // Wait for upload dialog to appear in the tab pane after authentication - cy.get('#upload-tab-pane', { timeout: 10000 }).should('be.visible'); - cy.get('#upload-tab-pane').should('contain', 'File Uploader'); - // Verify the drag-and-drop zone is present - cy.get('#drag-and-drop-zone', { timeout: 5000 }).should('be.visible'); - }) - -}); diff --git a/cypress/cypress/fixtures/example.json b/cypress/cypress/fixtures/example.json deleted file mode 100644 index 02e425437..000000000 --- a/cypress/cypress/fixtures/example.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name": "Using fixtures to represent data", - "email": "hello@cypress.io", - "body": "Fixtures are a great way to mock data for responses to routes" -} diff --git a/cypress/cypress/support/commands.js b/cypress/cypress/support/commands.js deleted file mode 100644 index 66ea16ef0..000000000 --- a/cypress/cypress/support/commands.js +++ /dev/null @@ -1,25 +0,0 @@ -// *********************************************** -// This example commands.js shows you how to -// create various custom commands and overwrite -// existing commands. -// -// For more comprehensive examples of custom -// commands please read more here: -// https://on.cypress.io/custom-commands -// *********************************************** -// -// -// -- This is a parent command -- -// Cypress.Commands.add('login', (email, password) => { ... }) -// -// -// -- This is a child command -- -// Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... }) -// -// -// -- This is a dual command -- -// Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... }) -// -// -// -- This will overwrite an existing command -- -// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... }) \ No newline at end of file diff --git a/cypress/cypress/support/e2e.js b/cypress/cypress/support/e2e.js deleted file mode 100644 index 0e7290a13..000000000 --- a/cypress/cypress/support/e2e.js +++ /dev/null @@ -1,20 +0,0 @@ -// *********************************************************** -// This example support/e2e.js is processed and -// loaded automatically before your test files. -// -// This is a great place to put global configuration and -// behavior that modifies Cypress. -// -// You can change the location of this file or turn off -// automatically serving support files with the -// 'supportFile' configuration option. -// -// You can read more here: -// https://on.cypress.io/configuration -// *********************************************************** - -// Import commands.js using ES2015 syntax: -import './commands' - -// Alternatively you can use CommonJS syntax: -// require('./commands') \ No newline at end of file diff --git a/cypress/e2e/spec.cy.js b/cypress/e2e/spec.cy.js index ca6a388bb..2a06d006c 100644 --- a/cypress/e2e/spec.cy.js +++ b/cypress/e2e/spec.cy.js @@ -171,11 +171,7 @@ describe('Navigation', () => { it('should navigate to different pages', () => { cy.visitGeoweaver('http://localhost:8070/Geoweaver/web/geoweaver'); // Skip intro if it appears - cy.get('body').then(($body) => { - if ($body.find('.introjs-skipbutton').length > 0) { - cy.get('.introjs-skipbutton').click(); - } - }); + cy.skipIntroIfPresent(); cy.contains('Log' , { timeout: 10000 }).click(); cy.contains('Logging').should('be.visible'); @@ -206,11 +202,7 @@ describe('Navigation', () => { describe('Host Testing', () => { it('should submit a create new host form successfully', () => { cy.visitGeoweaver('http://localhost:8070/Geoweaver/web/geoweaver'); - cy.get('body').then(($body) => { - if ($body.find('.introjs-skipbutton').length > 0) { - cy.get('.introjs-skipbutton').click(); - } - }); + cy.skipIntroIfPresent(); cy.get('#newhost').click(); cy.get('#hostip').type('1.1.1.1'); @@ -224,11 +216,7 @@ describe('Host Testing', () => { it('Search result should be successful after creating the host', () => { /* ==== Generated with Cypress Studio ==== */ cy.visitGeoweaver('http://localhost:8070/Geoweaver/web/geoweaver'); - cy.get('body').then(($body) => { - if ($body.find('.introjs-skipbutton').length > 0) { - cy.get('.introjs-skipbutton').click(); - } - }); + cy.skipIntroIfPresent(); cy.get('[data-intro="All the other tools"]').click(); cy.get('#toolbar-search').click(); cy.get('#keywords').clear('New Host'); @@ -251,11 +239,7 @@ describe('Host Testing', () => { describe('Process Testing', () => { it('Create Shell Process', () => { cy.visitGeoweaver('http://localhost:8070/Geoweaver/web/geoweaver'); - cy.get('body').then(($body) => { - if ($body.find('.introjs-skipbutton').length > 0) { - cy.get('.introjs-skipbutton').click(); - } - }); + cy.skipIntroIfPresent(); cy.get('#newprocess').click().then(() => { // Ensure the window or form is fully loaded and active cy.get('form', { timeout: 10000 }).should('be.visible'); // Wait until the form is visible @@ -279,11 +263,7 @@ describe('Process Testing', () => { cy.visitGeoweaver('http://localhost:8070/Geoweaver/web/geoweaver'); // Skip intro and go to process creation - cy.get('body').then(($body) => { - if ($body.find('.introjs-skipbutton').length > 0) { - cy.get('.introjs-skipbutton').click(); - } - }); + cy.skipIntroIfPresent(); cy.get('#newprocess').click(); // Ensure that the new process code area is visible before proceeding @@ -313,11 +293,7 @@ describe('Process Testing', () => { describe('Add Process to Weaver', () => { it('Add to weaver', () => { cy.visitGeoweaver('http://localhost:8070/Geoweaver/web/geoweaver'); - cy.get('body').then(($body) => { - if ($body.find('.introjs-skipbutton').length > 0) { - cy.get('.introjs-skipbutton').click(); - } - }).then( + cy.skipIntroIfPresent().then( () => { cy.get('#process_folder_shell', { timeout: 10000 }).should('be.visible'); } @@ -335,11 +311,7 @@ describe('Add Process to Weaver', () => { describe('Edit Process Name', () => { it('Add to weaver', () => { cy.visitGeoweaver('http://localhost:8070/Geoweaver/web/geoweaver'); - cy.get('body').then(($body) => { - if ($body.find('.introjs-skipbutton').length > 0) { - cy.get('.introjs-skipbutton').click(); - } - }).then( + cy.skipIntroIfPresent().then( () => { cy.get('#process_folder_shell', { timeout: 10000 }).should('be.visible'); } @@ -365,11 +337,7 @@ describe('Edit Process Name', () => { }) it('process category and id should be disabled', () => { cy.visitGeoweaver('http://localhost:8070/Geoweaver/web/geoweaver'); - cy.get('body').then(($body) => { - if ($body.find('.introjs-skipbutton').length > 0) { - cy.get('.introjs-skipbutton').click(); - } - }).then( + cy.skipIntroIfPresent().then( () => { cy.get('#process_folder_shell', { timeout: 10000 }).should('be.visible'); } @@ -382,11 +350,7 @@ describe('Edit Process Name', () => { it('Create and Edit Python Process', function() { cy.visitGeoweaver('http://localhost:8070/Geoweaver/web/geoweaver'); - cy.get('body').then(($body) => { - if ($body.find('.introjs-skipbutton').length > 0) { - cy.get('.introjs-skipbutton').click(); - } - }); + cy.skipIntroIfPresent(); cy.get('#process_folder_python').click(); cy.get('#newprocess').click(); cy.get('form select.form-control.form-control-sm').select('python'); @@ -496,11 +460,7 @@ describe('Edit Process Name', () => { describe('Delete Process', () => { it('Delete Shell Process', () => { cy.visitGeoweaver('http://localhost:8070/Geoweaver/web/geoweaver'); - cy.get('body').then(($body) => { - if ($body.find('.introjs-skipbutton').length > 0) { - cy.get('.introjs-skipbutton').click(); - } - }).then( + cy.skipIntroIfPresent().then( () => { cy.get('#processes', { timeout: 5000 }).should('be.visible'); } @@ -520,11 +480,7 @@ describe('Delete Process', () => { it('Delete Python Process', () => { cy.visitGeoweaver('http://localhost:8070/Geoweaver/web/geoweaver'); - cy.get('body').then(($body) => { - if ($body.find('.introjs-skipbutton').length > 0) { - cy.get('.introjs-skipbutton').click(); - } - }).then( + cy.skipIntroIfPresent().then( () => { cy.get('#process_folder_python', { timeout: 10000 }).should("be.visible") } @@ -567,11 +523,7 @@ describe('Write Password into .secret', () => { describe('Create Python process and run it', () => { it('creates python process and runs', () => { cy.visitGeoweaver('http://localhost:8070/Geoweaver/web/geoweaver'); - cy.get('body').then(($body) => { - if ($body.find('.introjs-skipbutton').length > 0) { - cy.get('.introjs-skipbutton').click(); - } - }); + cy.skipIntroIfPresent(); cy.get('#newprocess').click(); cy.get('form select.form-control.form-control-sm').select('Python'); @@ -659,11 +611,7 @@ describe('Create Python process and run it', () => { describe('Hosts Testing', () => { it('Create New Host', () => { cy.visitGeoweaver('http://localhost:8070/Geoweaver/web/geoweaver'); - cy.get('body').then(($body) => { - if ($body.find('.introjs-skipbutton').length > 0) { - cy.get('.introjs-skipbutton').click(); - } - }); + cy.skipIntroIfPresent(); cy.get('#newhost').click(); cy.get('#hostip').clear('1'); cy.get('#hostip').type('1.1.1.1'); @@ -677,11 +625,7 @@ describe('Hosts Testing', () => { it('Delete New Host', () => { cy.visitGeoweaver('http://localhost:8070/Geoweaver/web/geoweaver'); - cy.get('body').then(($body) => { - if ($body.find('.introjs-skipbutton').length > 0) { - cy.get('.introjs-skipbutton').click(); - } - }); + cy.skipIntroIfPresent(); cy.get('#host_folder_ssh > a').click(); cy.get('ul#host_folder_ssh_target').contains('New Host').click(); // Delete button now uses fa-trash icon instead of fa-minus, and has "Delete" text @@ -691,11 +635,7 @@ describe('Hosts Testing', () => { it('LocalHost testing', () => { cy.visitGeoweaver('http://localhost:8070/Geoweaver/web/geoweaver'); - cy.get('body').then(($body) => { - if ($body.find('.introjs-skipbutton').length > 0) { - cy.get('.introjs-skipbutton').click(); - } - }); + cy.skipIntroIfPresent(); cy.get('#host_folder_ssh > a').click(); cy.get('#host-100001').click(); cy.get('#_host_name').should('have.value', 'Localhost'); @@ -709,11 +649,7 @@ describe('Hosts Testing', () => { it('LocalHost Read Python Env', () => { cy.visitGeoweaver('http://localhost:8070/Geoweaver/web/geoweaver'); - cy.get('body').then(($body) => { - if ($body.find('.introjs-skipbutton').length > 0) { - cy.get('.introjs-skipbutton').click(); - } - }); + cy.skipIntroIfPresent(); cy.get('#host_folder_ssh > a').click(); cy.get('#host-100001').click(); // Click on Python Env tab instead of .fab diff --git a/cypress/support/commands.js b/cypress/support/commands.js index d80d07e1b..cf415b15d 100644 --- a/cypress/support/commands.js +++ b/cypress/support/commands.js @@ -66,14 +66,14 @@ Cypress.Commands.add('visitGeoweaver', (url = 'http://localhost:8070/Geoweaver/w cy.url({ timeout: 5000 }).then((currentUrl) => { if (currentUrl.includes('/localhost-login')) { // We're on login page, need to login - cy.get('input[type="password"]', { timeout: 10000 }).should('be.visible'); + cy.get('#password', { timeout: 10000 }).should('be.visible'); // Get the password from env or use default const password = Cypress.env('localhost_password') || '123456'; // Fill in password and submit - cy.get('input[type="password"]').type(password); - cy.get('button').contains('Login', { timeout: 10000 }).click(); + cy.get('#password').type(password); + cy.get('#loginForm button[type="submit"]').contains('Login').click(); // Wait for redirect to main page cy.url({ timeout: 10000 }).should('not.include', '/localhost-login'); @@ -85,4 +85,15 @@ Cypress.Commands.add('visitGeoweaver', (url = 'http://localhost:8070/Geoweaver/w // Wait a bit more for IntroJS to potentially start cy.wait(1500); +}); + +/** + * Skip IntroJS tour when the skip button is visible. + */ +Cypress.Commands.add('skipIntroIfPresent', () => { + cy.get('body', { timeout: 10000 }).then(($body) => { + if ($body.find('.introjs-skipbutton').length > 0) { + cy.get('.introjs-skipbutton').click(); + } + }); }); \ No newline at end of file From 3c9d989701fd266592506404136598a773be8c48 Mon Sep 17 00:00:00 2001 From: Ziheng Sun Date: Sat, 29 Aug 2026 00:33:52 -0400 Subject: [PATCH 06/10] Stabilize AsyncExportManager status transition test against CI races. Hold the mocked download with a latch so PENDING/PROCESSING can be observed reliably before asserting COMPLETED. Co-authored-by: Cursor --- .../com/gw/tools/AsyncExportManagerTest.java | 50 +++++++++++++------ 1 file changed, 34 insertions(+), 16 deletions(-) diff --git a/src/test/java/com/gw/tools/AsyncExportManagerTest.java b/src/test/java/com/gw/tools/AsyncExportManagerTest.java index 4fc1799f3..4e952925f 100644 --- a/src/test/java/com/gw/tools/AsyncExportManagerTest.java +++ b/src/test/java/com/gw/tools/AsyncExportManagerTest.java @@ -144,31 +144,49 @@ void testGetUserExportTasksEmpty() { @Test @Timeout(10) void testExportTaskStatusTransitions() throws Exception { - // Given + // Given — block download until we have observed PENDING String workflowId = "workflow123"; String userId = "user123"; String option = "workflowwithprocesscodehistory"; + java.util.concurrent.CountDownLatch proceed = + new java.util.concurrent.CountDownLatch(1); + + when(workflowTool.download(workflowId, option)).thenAnswer(invocation -> { + proceed.await(5, java.util.concurrent.TimeUnit.SECONDS); + return "download/temp/workflow123.zip"; + }); // When String taskId = asyncExportManager.startAsyncExport(workflowId, userId, option); - // Then - AsyncExportManager.ExportTask task = asyncExportManager.getExportTask(taskId); + // Then — wait until worker has set PROCESSING (or still PENDING briefly) + AsyncExportManager.ExportTask task = null; + for (int i = 0; i < 50; i++) { + task = asyncExportManager.getExportTask(taskId); + assertNotNull(task); + if (task.getStatus() == AsyncExportManager.ExportStatus.PENDING + || task.getStatus() == AsyncExportManager.ExportStatus.PROCESSING) { + break; + } + Thread.sleep(10); + } assertNotNull(task); - assertEquals(AsyncExportManager.ExportStatus.PENDING, task.getStatus()); - - // Wait for task to start processing - try { - Thread.sleep(50); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); + assertTrue( + task.getStatus() == AsyncExportManager.ExportStatus.PENDING + || task.getStatus() == AsyncExportManager.ExportStatus.PROCESSING, + "Expected PENDING or PROCESSING before download finishes, was: " + task.getStatus()); + + // Allow download to complete and wait for COMPLETED + proceed.countDown(); + for (int i = 0; i < 50; i++) { + task = asyncExportManager.getExportTask(taskId); + if (task.getStatus() == AsyncExportManager.ExportStatus.COMPLETED + || task.getStatus() == AsyncExportManager.ExportStatus.FAILED) { + break; + } + Thread.sleep(20); } - - // The task should be in PROCESSING or COMPLETED status - task = asyncExportManager.getExportTask(taskId); - assertTrue(task.getStatus() == AsyncExportManager.ExportStatus.PROCESSING || - task.getStatus() == AsyncExportManager.ExportStatus.COMPLETED || - task.getStatus() == AsyncExportManager.ExportStatus.FAILED); + assertEquals(AsyncExportManager.ExportStatus.COMPLETED, task.getStatus()); } @Test From de7526e2d29b287611a8dac04561be851f0592b2 Mon Sep 17 00:00:00 2001 From: Ziheng Sun Date: Sat, 29 Aug 2026 00:37:51 -0400 Subject: [PATCH 07/10] Point Cypress supportFile at support/e2e.js for cypress project root. Working-directory ./cypress made the default cypress/support path miss the real support file and abort the run. Co-authored-by: Cursor --- cypress/cypress.config.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cypress/cypress.config.js b/cypress/cypress.config.js index f7db48fd6..a70cfb88c 100644 --- a/cypress/cypress.config.js +++ b/cypress/cypress.config.js @@ -11,6 +11,8 @@ module.exports = defineConfig({ }, e2e: { experimentalStudio: true, + supportFile: 'support/e2e.js', + specPattern: 'e2e/**/*.cy.{js,jsx,ts,tsx}', setupNodeEvents(on, config) { on('task', { log(message) { @@ -23,11 +25,9 @@ module.exports = defineConfig({ on('after:screenshot', (details) => { console.log('Screenshot taken:', details); return null; - });} - + }); }, + }, screenshotsFolder: 'screenshots', videosFolder: 'videos', - - }) From 05cf18f7f4761ee8b6e65f422a36a95f3c7d6e24 Mon Sep 17 00:00:00 2001 From: Ziheng Sun Date: Sat, 29 Aug 2026 00:46:02 -0400 Subject: [PATCH 08/10] Fix Cypress process edit/run tests for collapsible details UI. Expand process-info-bar via Details before editing name/id, and click Run by title instead of a brittle onclick selector. Co-authored-by: Cursor --- cypress/e2e/spec.cy.js | 22 ++++++++-------------- cypress/support/commands.js | 10 ++++++++++ 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/cypress/e2e/spec.cy.js b/cypress/e2e/spec.cy.js index 2a06d006c..b587f75ef 100644 --- a/cypress/e2e/spec.cy.js +++ b/cypress/e2e/spec.cy.js @@ -321,16 +321,13 @@ describe('Edit Process Name', () => { cy.get('#process_folder_shell', { timeout: 10000 }).should('be.visible'); } ); - cy.get('ul#process_folder_shell_target').contains('shell_test').click().then( - () => { - cy.get('#processname', { timeout: 10000 }).should('be.visible'); - } - ); + cy.get('ul#process_folder_shell_target').contains('shell_test').click(); + cy.expandProcessDetails(); cy.get('#processname').should('be.visible').and('not.be.disabled'); cy.get('#processname').clear(); - cy.get('#processname', { timeout: 10000 }).should('be.visible'); // Waits for up to 10 seconds - cy.get('#processname').type('updated_shell_test', { force: true }) // Type the text - .should('have.value', 'updated_shell_test'); // Check the value + cy.get('#processname', { timeout: 10000 }).should('be.visible'); + cy.get('#processname').type('updated_shell_test', { force: true }) + .should('have.value', 'updated_shell_test'); cy.get('.process-edit-right-icon').click(); cy.get('ul#process_folder_shell_target').should('contain', 'updated_shell_test'); @@ -344,6 +341,7 @@ describe('Edit Process Name', () => { ); cy.get('#process_folder_shell').click(); cy.get('ul#process_folder_shell_target').contains('updated_shell_test').click(); + cy.expandProcessDetails(); cy.get('#processcategory').should('be.disabled'); cy.get('#processid').should('be.disabled') }) @@ -582,12 +580,8 @@ describe('Create Python process and run it', () => { cy.log("now should change the content") - cy.get('#processid').then(($input) => { - const processId = $input.val(); // Get the value of the input field - console.log('process id ',processId) - const selector = `[onclick="GW.process.runProcess('${processId}', 'hello_world.py', 'python')"]`; - cy.get(selector).click(); // Perform actions using the dynamically constructed selector - }); + // Run button onclick includes a trailing semicolon; prefer stable title selector + cy.get('button[title="Run Process"]', { timeout: 10000 }).should('be.visible').click(); cy.intercept('POST', '/Geoweaver/web/executeProcess').as('executeProcess'); // Intercept the POST request cy.get('#host-execute-btn').click(); diff --git a/cypress/support/commands.js b/cypress/support/commands.js index cf415b15d..ddf51d70b 100644 --- a/cypress/support/commands.js +++ b/cypress/support/commands.js @@ -96,4 +96,14 @@ Cypress.Commands.add('skipIntroIfPresent', () => { cy.get('.introjs-skipbutton').click(); } }); +}); + +/** + * Expand the collapsible process details bar so #processname / #processid are visible. + */ +Cypress.Commands.add('expandProcessDetails', () => { + cy.get('#main-process-info', { timeout: 10000 }).should('be.visible'); + cy.get('#toggle-details-btn', { timeout: 10000 }).should('be.visible').click(); + cy.get('#process-info-bar', { timeout: 10000 }).should('be.visible'); + cy.get('#processname', { timeout: 10000 }).should('be.visible'); }); \ No newline at end of file From ce602b23cd37844e42319bf5756a053bca53367a Mon Sep 17 00:00:00 2001 From: Ziheng Sun Date: Sat, 29 Aug 2026 01:05:04 -0400 Subject: [PATCH 09/10] Harden Cypress python create-and-run test against collapsed sidebar races. Wait for add/process, open the python folder via collapse(show), and use stable name/run selectors before asserting console output. Co-authored-by: Cursor --- cypress/e2e/spec.cy.js | 87 ++++++++++++++++++------------------------ 1 file changed, 38 insertions(+), 49 deletions(-) diff --git a/cypress/e2e/spec.cy.js b/cypress/e2e/spec.cy.js index b587f75ef..41a666011 100644 --- a/cypress/e2e/spec.cy.js +++ b/cypress/e2e/spec.cy.js @@ -524,79 +524,68 @@ describe('Create Python process and run it', () => { cy.skipIntroIfPresent(); cy.get('#newprocess').click(); - cy.get('form select.form-control.form-control-sm').select('Python'); - cy.get('form > :nth-child(1) > :nth-child(4)').type('hello_world.py'); + cy.get('.new-process-code-area', { timeout: 10000 }).should('be.visible'); + cy.get('form select.form-control.form-control-sm').select('python'); + cy.get('[id^="processname-"]').clear().type('hello_world.py'); + cy.intercept('POST', '**/add/process').as('addProcess'); cy.get('.modal-footer').contains('Add').click(); + cy.wait('@addProcess').its('response.statusCode').should('eq', 200); - cy.get('ul#process_folder_python_target').contains('hello_world.py').click(); + // Add opens process details automatically; ensure python folder is expanded for list check + cy.get('#main-process-info', { timeout: 10000 }).should('be.visible'); + cy.window().then((win) => { + win.$('#process_folder_python_target').collapse('show'); + }); + cy.get('ul#process_folder_python_target', { timeout: 10000 }) + .should('be.visible') + .and('contain', 'hello_world.py'); cy.window().then((win) => { - return new Cypress.Promise((resolve) => { + return new Cypress.Promise((resolve, reject) => { + const started = Date.now(); const checkMonaco = () => { - const editors = win.monaco?.editor?.getEditors(); // Get all Monaco editors - cy.log(`Total Monaco Editors found: ${editors?.length || 0}`); // Log editor count - + const editors = win.monaco?.editor?.getEditors(); if (editors && editors.length > 0) { - const visibleEditor = editors.find((editor) => { - const isVisible = Cypress.$(editor.getDomNode()).is(':visible'); // Check if visible - cy.log(`Editor visible: ${isVisible}`); - return isVisible; - }); - + const visibleEditor = editors.find((editor) => + Cypress.$(editor.getDomNode()).is(':visible') + ); if (visibleEditor) { - cy.log("Using visible Monaco editor"); resolve(visibleEditor); - } else { - cy.log("No visible Monaco editor found, retrying..."); - setTimeout(checkMonaco, 500); // Retry if no visible editor is found + return; } - } else { - setTimeout(checkMonaco, 500); // Retry if no editors are found } + if (Date.now() - started > 15000) { + reject(new Error('Timed out waiting for Monaco editor')); + return; + } + setTimeout(checkMonaco, 500); }; checkMonaco(); }); }).then((editor) => { - if (editor) { - cy.log("Found visible Monaco Editor:", editor); - const model = editor.getModel(); - if (model) { - cy.wrap(null).should(() => { - editor.focus(); // Ensure the editor is focused - model.setValue("print('hello world!')"); // Set value - editor.trigger("keyboard", "type", { text: "" }); // Simulate keystroke to force UI update - }); - - // **Wait until Monaco editor actually reflects the new value** - cy.wrap(null).should(() => { - expect(model.getValue().trim()).to.equal("print('hello world!')"); - }); - } - } - }); - - cy.get('#main-process-info', { timeout: 10000 }).should("be.visible") - - cy.log("now should change the content") + const model = editor.getModel(); + expect(model).to.exist; + editor.focus(); + model.setValue("print('hello world!')"); + editor.trigger('keyboard', 'type', { text: '' }); + cy.wrap(null).should(() => { + expect(model.getValue().trim()).to.equal("print('hello world!')"); + }); + }); - // Run button onclick includes a trailing semicolon; prefer stable title selector cy.get('button[title="Run Process"]', { timeout: 10000 }).should('be.visible').click(); - cy.intercept('POST', '/Geoweaver/web/executeProcess').as('executeProcess'); // Intercept the POST request + cy.intercept('POST', '**/executeProcess').as('executeProcess'); cy.get('#host-execute-btn').click(); cy.get('#process-confirm-btn').click(); - cy.get('#inputpswd').clear('1'); - cy.get('#inputpswd').type('123456'); + cy.get('#inputpswd').clear().type('123456'); cy.get('#pswd-confirm-btn').click(); - cy.wait(5000); - - cy.get('#single-console-content').should('contain', 'hello world!'); + cy.wait('@executeProcess').its('response.statusCode').should('eq', 200); + cy.get('#single-console-content', { timeout: 30000 }).should('contain', 'hello world!'); cy.get('#single-console-content').should('contain', 'Exit Code: 0'); - cy.get('#process-log-window').click(); cy.get('#process-log-window').should('be.visible'); - cy.wait('@executeProcess').its('response.statusCode').should('eq', 200); }); }); From 8b721112706bd2b13ad36190c2d8638e0507e603 Mon Sep 17 00:00:00 2001 From: Ziheng Sun Date: Sat, 29 Aug 2026 01:12:35 -0400 Subject: [PATCH 10/10] Document Java 17 / Spring Boot 3 requirements for new and existing users. Add a migration guide and refresh install, upgrade, README, logging, and mkdocs nav so users know when to use 2.2+ vs legacy 2.1.x. Co-authored-by: Cursor --- README.md | 23 +++++-- docs/geoweaver-dmg-script.md | 8 +-- docs/index.md | 6 +- docs/install.md | 124 ++++++++++++++++++++--------------- docs/java17-migration.md | 82 +++++++++++++++++++++++ docs/logging.md | 44 ++++--------- docs/upgrade.md | 52 +++++++++------ mkdocs.yml | 18 ++++- 8 files changed, 239 insertions(+), 118 deletions(-) create mode 100644 docs/java17-migration.md diff --git a/README.md b/README.md index 7e44548f4..de56eeaac 100644 --- a/README.md +++ b/README.md @@ -94,9 +94,17 @@ For detailed information, visit [Geoweaver Documentation](https://geoweaver.dev) ### Prerequisites -- ☕ Java **17 or higher** (OpenJDK 17+). Latest Geoweaver (2.2+ / Spring Boot 3) **does not support JDK < 17**. -- If you cannot upgrade Java, use an older Geoweaver release (**2.1.x**, Java 11): [Releases](https://github.com/ESIPFed/Geoweaver/releases) (example: [v2.1.7 jar](https://github.com/ESIPFed/Geoweaver/releases/download/v2.1.7/geoweaver.jar)). -- 🐳 Docker (optional, for Docker installation) +- ☕ Java **17 or higher** (OpenJDK / Temurin 17+). Geoweaver **2.2+** (Spring Boot 3) **does not support JDK < 17**. +- If you cannot upgrade Java, stay on **Geoweaver 2.1.x** (Java 11): [Releases](https://github.com/ESIPFed/Geoweaver/releases) (example: [v2.1.7 jar](https://github.com/ESIPFed/Geoweaver/releases/download/v2.1.7/geoweaver.jar)). +- 🐳 Docker (optional) + +### What should I do? + +| Situation | Recommendation | +|-----------|----------------| +| **New install** | Install Java 17+, then use Quick Start below ([details](docs/install.md)). | +| **Already on 2.1.x, can use Java 17** | Back up `~/h2`, upgrade JDK, then upgrade jar / pygeoweaver ([migration guide](docs/java17-migration.md), [upgrade](docs/upgrade.md)). | +| **Must stay on Java 11** | Keep Geoweaver **2.1.x**; do not run the latest 2.2+ jar. | ### Quick Start @@ -106,14 +114,15 @@ For detailed information, visit [Geoweaver Documentation](https://geoweaver.dev) # Install PyGeoweaver pip install pygeoweaver --upgrade -# Start Geoweaver +# Start Geoweaver (needs Java 17+ for current releases) gw start ``` #### ☕ Java Method -1. Download [geoweaver.jar](https://github.com/ESIPFed/Geoweaver/releases/download/latest/geoweaver.jar) -2. Run: `java -jar geoweaver.jar` +1. Install Java 17+ +2. Download [geoweaver.jar](https://github.com/ESIPFed/Geoweaver/releases/download/latest/geoweaver.jar) +3. Run: `java -jar geoweaver.jar` #### 🐳 Docker Method @@ -127,7 +136,7 @@ docker run -t -i -p 8070:8070 geoweaver/geoweaver > 📝 Access Geoweaver at http://localhost:8070/Geoweaver -[Detailed Installation Guide](docs/install.md) +[Detailed Installation Guide](docs/install.md) · [Java 17 migration](docs/java17-migration.md) · [Upgrading](docs/upgrade.md) # Demo diff --git a/docs/geoweaver-dmg-script.md b/docs/geoweaver-dmg-script.md index 834c2f825..a636df04c 100644 --- a/docs/geoweaver-dmg-script.md +++ b/docs/geoweaver-dmg-script.md @@ -25,11 +25,11 @@ show_error_message: Displays an error message dialog using AppleScript. move_existing_data: Moves existing Geoweaver data to a backup directory if necessary. -install_jdk_based_on_architecture: Installs the appropriate JDK version based on system architecture. +install_jdk_based_on_architecture: Installs the appropriate JDK version based on system architecture (Geoweaver 2.2+ expects **JDK 17+**). -install_jdk: Downloads and installs a specified JDK version. +install_jdk: Downloads and installs a specified JDK version (prefer Temurin / OpenJDK **17** or newer). -check_jdk: Checks if a compatible JDK is installed and installs it if necessary. +check_jdk: Checks if a compatible JDK (**17+** for current Geoweaver) is installed and installs it if necessary. ### Main Script Logic @@ -37,7 +37,7 @@ Display Loading Screen: Shows a loading screen to indicate the startup process. Load Configuration: Loads configuration settings from a file, or creates a new configuration file if it doesn't exist. -Check and Install JDK: Ensures a compatible JDK is installed, installing it if necessary. +Check and Install JDK: Ensures a compatible JDK (**17+** for Geoweaver 2.2+) is installed, installing it if necessary. Stop Existing Instances: Stops any existing Geoweaver instances to prevent conflicts. diff --git a/docs/index.md b/docs/index.md index 397c1788b..f575ce258 100644 --- a/docs/index.md +++ b/docs/index.md @@ -31,7 +31,11 @@ Only two fundamental objectives: * [Jupyter Recording](https://andrewmagill.github.io/#/) # Installation -[Click here](install.md) for the steps +[Click here](install.md) for the steps. + +**Java 17 required for Geoweaver 2.2+.** +New and existing users: see [Java 17 / Spring Boot 3 guidance](java17-migration.md). +Upgrading from 2.1.x: [Upgrading Geoweaver](upgrade.md). # Demo [A live demo site](https://geobrain.csiss.gmu.edu/Geoweaver) is available. diff --git a/docs/install.md b/docs/install.md index dc74aee63..d5f0a058c 100644 --- a/docs/install.md +++ b/docs/install.md @@ -3,127 +3,145 @@ ## Dependencies -Java **17+** (OpenJDK 17 or higher). Latest Geoweaver (2.2+ / Spring Boot 3) **no longer supports JDK < 17**. +- **Java 17+** (OpenJDK / Temurin 17 or higher) for Geoweaver **2.2+** (Spring Boot 3). +- **Docker** [18.09.1+](https://docs.docker.com/install/) only if you install via Docker. -If you cannot bump your JDK, stay on **Geoweaver 2.1.x** (Java 11): -see [GitHub Releases](https://github.com/ESIPFed/Geoweaver/releases) -(example jar: [v2.1.7](https://github.com/ESIPFed/Geoweaver/releases/download/v2.1.7/geoweaver.jar)). +> **JDK < 17 is not supported** by the latest Geoweaver. +> If you cannot upgrade Java, use **Geoweaver 2.1.x** (Java 11). See [Java 17 migration guide](java17-migration.md) and [Releases](https://github.com/ESIPFed/Geoweaver/releases) (example: [v2.1.7 jar](https://github.com/ESIPFed/Geoweaver/releases/download/v2.1.7/geoweaver.jar)). -[Docker](https://docs.docker.com/install/) 18.09.1+ (only for installation via docker) +Check your runtime: + +```shell +java -version +``` + +## Who should read what? + +| You are… | Do this | +|----------|---------| +| **New user** | Install Java 17+, then use Quick Install below. | +| **Existing 2.1.x user upgrading** | Follow [Upgrading](upgrade.md) and [Java 17 migration](java17-migration.md) (back up `~/h2` first). | +| **Stuck on Java 11** | Stay on Geoweaver **2.1.x**; do not use the latest 2.2+ jar until JDK 17 is available. | ## Quick Install -(Applicable in Linux, Mac, and Windows) +(Applicable on Linux, macOS, and Windows) -### Python Way +### Python Way (recommended) -If you have Python installed already, please do: +If you have Python installed: -**Step 1**: Open a Terminal/Prompt and run +**Step 1**: Open a Terminal/Prompt and run: ```shell pip install pygeoweaver --upgrade ``` -**Step 2**: After pygeoweaver is installed, please run: +**Step 2**: After pygeoweaver is installed, run: ```shell gw start ``` -**Step 3**: Open browser and enter: http://localhost:8070/Geoweaver/ . That is it. +Recent pygeoweaver versions expect **Java 17+** for the current Geoweaver line and will warn / exit if the JDK is too old. + +**Step 3**: Open a browser: http://localhost:8070/Geoweaver/ ### Java Way -If you don't have Python only have JDK installed, please follow: +If you only have a JDK (no Python): -**Step 1**: Download the latest version of [geoweaver.jar](https://github.com/ESIPFed/Geoweaver/releases/download/latest/geoweaver.jar) and save it in any folder +**Step 1**: Install **Java 17+**, then download the latest [geoweaver.jar](https://github.com/ESIPFed/Geoweaver/releases/download/latest/geoweaver.jar). -**Step 2**: Navigate to the Jar file folder and Run the command: +**Step 2**: From the folder that contains the jar: ```shell java -jar geoweaver.jar ``` -**Step 3**: Open browser and enter: http://localhost:8070/Geoweaver/ .That's it! +**Step 3**: Open http://localhost:8070/Geoweaver/ + +To stay on the Java 11–compatible line, download a **2.1.x** jar from [Releases](https://github.com/ESIPFed/Geoweaver/releases) instead of `latest`. ## Build from source -Use maven to build. In the command line, go to the root folder and execute `mvn install`. After a successful build, the Geoweaver jar package will be under the directory: `Geoweaver/target/Geoweaver-.jar`. +Requires **JDK 17+** and Maven. -## Install using Docker +```shell +mvn clean install +java -jar target/geoweaver.jar +``` -**Step 1**: Install Docker Desktop if you haven't (please refer to [Get Docker](https://docs.docker.com/get-docker/) to download). +The package is produced under `Geoweaver/target/` (e.g. `geoweaver.jar` / versioned artifact). -**Step 2**: Pull geoweaver image using command: +## Install using Docker -`docker pull geoweaver/geoweaver` +**Step 1**: Install Docker Desktop if you haven't (see [Get Docker](https://docs.docker.com/get-docker/)). -for other versions, visit [docker hub](https://hub.docker.com/repository/docker/geoweaver/geoweaver/general) +**Step 2**: Pull the image: -**Step 3**: Run geoweaver using command: +```shell +docker pull geoweaver/geoweaver +``` -`docker run -t -i -v :/home/marsvegan/ -p 8070:8070 -e PASSWORD="YOUR_PASSWORD" geoweaver/geoweaver` +For other tags, see [Docker Hub](https://hub.docker.com/repository/docker/geoweaver/geoweaver/general). -> *What is going on?* : `-v :/home/marsvegan/` is for mounting your current home directory into the docker containers. `marsvegan` is the user name within geoweaver containers. `-p 8070:8070` maps the port so you can access Geoweaver from your browser. `-e` is for specifying environment variable `PASSWORD`. `geoweaver/geoweaver` is the published docker image url in DockerHub. +**Step 3**: Run: -*Tip*: You can create an alias, so next time, you won't copy and paste such a long command: +```shell +docker run -t -i -v :/home/marsvegan/ -p 8070:8070 -e PASSWORD="YOUR_PASSWORD" geoweaver/geoweaver +``` -`alias geoweaver="docker run -t -i -v :/home/marsvegan/ -p 8070:8070 geoweaver/geoweaver"` +> `-v :/home/marsvegan/` mounts your home directory into the container. `marsvegan` is the user inside the image. `-p 8070:8070` exposes the UI. `-e PASSWORD=...` sets the localhost password. +Tip — create an alias: -Then start it by simply typing: `geoweaver` +```shell +alias geoweaver="docker run -t -i -v :/home/marsvegan/ -p 8070:8070 geoweaver/geoweaver" +``` -**Step 4**: Open web browser and input `http://localhost:8070/Geoweaver`. Geoweaver should show up in a sec. That is it! +**Step 4**: Open http://localhost:8070/Geoweaver/ ## Reset Password for Localhost -Geoweaver will automatically create a password for localhost. It will only show once at the first run of Geoweaver. It is recommended to copy and save it in a safe place. If you forget or miss that password, please run the following command to reset it: +Geoweaver creates a localhost password on first run (shown once). To reset: -``` +```shell java -jar geoweaver.jar resetpassword ``` -If you used `Docker`, use the following command: +With Docker: -``` +```shell docker run -t -i -v :/home/marsvegan/ -p 8070:8070 jensensun/geoweaver resetpassword ``` ## Set up HTTP Proxy -[Click here](http-proxy.md) to set up +See [HTTP proxy](http-proxy.md). ## Changing the listening port -Geoweaver allows one to change the listening port to run multiple instances simultaneously on the same machine. -Default port used by Geoweaver is 8070, to change the port please run the following command: +Default port is **8070**. To change it: #### Using Environment Variable -**For Linux/macOS**: -``` -export GEOWEAVER_PORT='Port number' -``` -***Example: export GEOWEAVER_PORT=8081*** +**Linux/macOS**: -**For Windows-Command Prompt**: -``` -set GEOWEAVER_PORT='Port number' +```shell +export GEOWEAVER_PORT=8081 ``` -***Example: set GEOWEAVER_PORT=8081*** -**For Windows-Powershell**: -``` -$env GEOWEAVER_PORT='Port number' +**Windows Command Prompt**: + +```shell +set GEOWEAVER_PORT=8081 ``` -***Example: $env GEOWEAVER_PORT=8081*** -Run the command above in a new terminal, then start the application(using start command) to launch a new instance of Geoweaver on your machine. +**Windows PowerShell**: +```shell +$env:GEOWEAVER_PORT=8081 ``` -java -jar Geoweaver.jar -``` - -After changing the port using the above command manually change the port number in url. +Then start Geoweaver and open the matching URL (e.g. http://localhost:8081/Geoweaver/). diff --git a/docs/java17-migration.md b/docs/java17-migration.md new file mode 100644 index 000000000..f889bb844 --- /dev/null +++ b/docs/java17-migration.md @@ -0,0 +1,82 @@ +# Java 17 / Spring Boot 3 — What You Should Do + +Geoweaver **2.2+** runs on **Spring Boot 3** and requires **Java 17 or newer**. +Geoweaver **2.1.x** remains the last line that supports **Java 11**. + +| Line | Runtime | Stack | Use when | +|------|---------|-------|----------| +| **2.2+ (current)** | Java **17+** | Spring Boot **3.3** | New installs, and upgrades when you can use JDK 17 | +| **2.1.x (legacy)** | Java **11+** | Spring Boot **2.x** | You cannot upgrade the system JDK yet | + +--- + +## New users (first-time install) + +1. **Install Java 17+** (Temurin / OpenJDK recommended). + ```bash + java -version + # Expect: 17, 21, or higher + ``` +2. Prefer **PyGeoweaver** (it can help install a JDK when needed): + ```bash + pip install pygeoweaver --upgrade + gw start + ``` +3. Or download the latest JAR and run: + ```bash + java -jar geoweaver.jar + ``` +4. Open **http://localhost:8070/Geoweaver/** + +Details: [Installation](install.md) + +--- + +## Existing users (already running Geoweaver 2.1.x) + +### Option A — Upgrade to 2.2+ (recommended when you can) + +1. **Upgrade the JDK to 17+** on every machine that runs the Geoweaver server (not required on remote SSH hosts that only execute jobs). +2. **Back up** your data before upgrading: + - Default H2 DB folder: `~/h2/` + - Workspace: `~/gw-workspace/` + - Logs: `~/geoweaver/logs/` (if present) +3. Upgrade the app: + - **Python:** `pip install pygeoweaver --upgrade` then `gw start --force` + - **JAR:** download [latest geoweaver.jar](https://github.com/ESIPFed/Geoweaver/releases/download/latest/geoweaver.jar) and restart with Java 17+ +4. If the H2 engine changed across versions and the DB will not start, follow the migration steps in [Upgrading](upgrade.md). +5. After start, confirm the UI loads and re-test a simple localhost process. + +### Option B — Stay on 2.1.x (cannot use Java 17 yet) + +1. **Do not** run a 2.2+ JAR with Java 11 — the process will exit with an unsupported-Java warning. +2. Keep using **Geoweaver 2.1.x** from [Releases](https://github.com/ESIPFed/Geoweaver/releases). +3. Example legacy JAR: [v2.1.7 geoweaver.jar](https://github.com/ESIPFed/Geoweaver/releases/download/v2.1.7/geoweaver.jar) + ```bash + java -jar geoweaver.jar # with Java 11 + ``` +4. **PyGeoweaver:** pin an older package / jar workflow that still targets 2.1.x, or upgrade the JDK before using the newest `gw start` that expects Java 17. + +--- + +## Developers / contributors + +- Build and CI use **JDK 17**. +- From source: + ```bash + mvn clean install + java -jar target/geoweaver.jar + ``` +- Cypress and unit tests in this branch assume Java 17 and Spring Boot 3 APIs. + +--- + +## Quick decision guide + +```text +Can you install Java 17+ on the Geoweaver server machine? + ├─ YES → Use Geoweaver 2.2+ (latest). Upgrade pygeoweaver / jar; back up ~/h2 first. + └─ NO → Stay on Geoweaver 2.1.x + Java 11. Do not download “latest” until you can upgrade JDK. +``` + +Questions or upgrade issues: [GitHub Issues](https://github.com/ESIPFed/Geoweaver/issues). diff --git a/docs/logging.md b/docs/logging.md index 49af205b1..da3bed234 100644 --- a/docs/logging.md +++ b/docs/logging.md @@ -1,47 +1,29 @@ ## Logging in Geoweaver -Geoweaver is developed using Spring Boot library, and its logging configuration is very similar to [Spring Boot Logging](https://docs.spring.io/spring-boot/docs/2.1.13.RELEASE/reference/html/boot-features-logging.html). +Geoweaver is built with Spring Boot. Logging follows the usual Spring Boot approach; see the current [Spring Boot logging documentation](https://docs.spring.io/spring-boot/reference/features/logging.html) (Boot 3.x). ## Change Logging Level in Source Code -1. Open Geoweaver folder in VS Code or any IDE. +1. Open the Geoweaver project in your IDE. -2. Open the file `Geoweaver/src/main/resources/log4j.properties` +2. Open `src/main/resources/log4j.properties` (or the active Logback / logging config used by your build). -3. Change the logging level in the following line to desired level: +3. Set the desired level for `com.gw`, for example: -```yaml +```properties log4j.logger.com.gw=DEBUG ``` -4. Save the `log4j.properties`. Restart Geoweaver by running `Geoweaver/src/main/java/com/gw/GeoweaverApplication.java`. +4. Restart Geoweaver (from the IDE or `java -jar …` with **Java 17+**). -You should now see logs printed in the files of `com.gw` packages, e.g.: +Example startup lines on a modern runtime: ```log -INFO 2023-10-17 18:34:56,912 0 com.gw.GeoweaverApplication [restartedMain] Starting GeoweaverApplication using Java 17.0.5 on jS-M1.local with PID 58059 (/Users/joe/Documents/GitHub/Geoweaver/target/classes started by joe in /Users/joe/Documents/GitHub/Geoweaver) -DEBUG 2023-10-17 18:34:56,913 1 com.gw.GeoweaverApplication [restartedMain] Running with Spring Boot v2.4.1, Spring v5.3.27 -INFO 2023-10-17 18:34:56,913 1 com.gw.GeoweaverApplication [restartedMain] No active profile set, falling back to default profiles: default -DEBUG 2023-10-17 18:35:08,111 11199 com.gw.workers.WorkerManager [restartedMain] The worknumber setting: 5 -DEBUG 2023-10-17 18:35:08,111 11199 com.gw.workers.WorkerManager [restartedMain] worker manager created a worker -DEBUG 2023-10-17 18:35:08,111 11199 com.gw.workers.WorkerManager [restartedMain] worker manager created a worker -DEBUG 2023-10-17 18:35:08,111 11199 com.gw.workers.WorkerManager [restartedMain] worker manager created a worker -DEBUG 2023-10-17 18:35:08,111 11199 com.gw.workers.WorkerManager [restartedMain] worker manager created a worker -DEBUG 2023-10-17 18:35:08,111 11199 com.gw.workers.WorkerManager [restartedMain] worker manager created a worker -DEBUG 2023-10-17 18:35:08,348 11436 com.gw.web.GoogleEarthController [restartedMain] A new Google Earth restTemplate is created -DEBUG 2023-10-17 18:35:08,353 11441 com.gw.web.JupyterController [restartedMain] A new restTemplate is created -DEBUG 2023-10-17 18:35:08,510 11598 com.gw.server.JupyterRedirectServlet [restartedMain] Initializing Jupyter Websocket Session... -DEBUG 2023-10-17 18:35:08,511 11599 com.gw.server.JupyterHubRedirectServlet [restartedMain] Initializing JupyterHub Websocket Session... -DEBUG 2023-10-17 18:35:08,511 11599 com.gw.server.JupyterLabRedirectServlet [restartedMain] Initializing Jupyter Lab Websocket Session... -INFO 2023-10-17 18:35:08,534 11622 com.gw.ssh.SecurityConfiguration$$EnhancerBySpringCGLIB$$9e6bebbd [restartedMain] registering SSH authentication provider -INFO 2023-10-17 18:35:09,091 12179 com.gw.GeoweaverApplication [restartedMain] Started GeoweaverApplication in 17.375 seconds (JVM running for 17.681) -DEBUG 2023-10-17 18:35:09,135 12223 com.gw.commands.TopEntryCommand [restartedMain] should print out all supported commands -INFO 2023-10-17 18:35:09,163 12251 com.gw.GeoweaverApplication [restartedMain] Public user exists. -DEBUG 2023-10-17 18:35:09,163 12251 com.gw.GeoweaverApplication [restartedMain] test what is going on -DEBUG 2023-10-17 18:35:09,163 12251 com.gw.tools.UserTool [restartedMain] Belong the no-owner resources to public user.. -INFO 2023-10-17 18:35:09,270 12358 com.gw.GeoweaverApplication [restartedMain] Localhost exists. -INFO 2023-10-17 18:35:09,270 12358 com.gw.utils.BaseTool [restartedMain] get existing workspace dir: ~/gw-workspace -INFO 2023-10-17 18:35:09,270 12358 com.gw.utils.BaseTool [restartedMain] new workspace dir: ~/gw-workspace +INFO com.gw.GeoweaverApplication - Starting GeoweaverApplication using Java 17 ... +INFO com.gw.GeoweaverApplication - Running with Spring Boot v3.3.x +INFO com.gw.GeoweaverApplication - Started GeoweaverApplication GeoWeaver is started and ready for use.. URL: http://localhost:8070/Geoweaver -``` \ No newline at end of file +``` + +Default file logs (when configured) typically live under `~/geoweaver/logs/`. diff --git a/docs/upgrade.md b/docs/upgrade.md index 356edbed4..915e6b5cf 100644 --- a/docs/upgrade.md +++ b/docs/upgrade.md @@ -1,72 +1,84 @@ -## Upgrading Geoweaver to Latest Version +## Upgrading Geoweaver + +> **Before you upgrade to 2.2+:** install **Java 17+**. Latest Geoweaver will refuse to start on older JDKs. +> Full guidance: [Java 17 migration guide](java17-migration.md). + +### Existing users checklist + +1. Confirm `java -version` reports **17 or higher**. +2. Back up `~/h2/` (database) and `~/gw-workspace/` (files). +3. Upgrade via Python or JAR (below). +4. If the database fails to open after the jump, use [Complex Upgrade (Database Migration)](#complex-upgrade-database-migration). + +If you **cannot** use Java 17, **do not upgrade** to 2.2+. Keep **Geoweaver 2.1.x** from [Releases](https://github.com/ESIPFed/Geoweaver/releases). ### Simple Upgrade #### Python Way -If you installed Geoweaver via the Python way, you can upgrade Geoweaver by simply running: - ```shell +pip install pygeoweaver --upgrade gw start --force ``` -The option `--force` will ensure the newest Geoweaver jar is downloaded before starting. +`--force` re-downloads the newest Geoweaver jar before start. The host JDK must already be 17+. #### Java Way -Upgrading Geoweaver to the latest version is a straightforward process. Follow these steps to perform a simple upgrade: - -- Download the new version of Geoweaver's JAR file: - ```shell wget https://github.com/ESIPFed/Geoweaver/releases/download/latest/geoweaver.jar +java -jar geoweaver.jar ``` -- Start Geoweaver with the updated JAR file: +Use a Java 17+ runtime for the `java` command above. + +### Staying on Geoweaver 2.1.x (Java 11) ```shell +# Example — pin a known 2.1.x jar (adjust version as needed) +wget https://github.com/ESIPFed/Geoweaver/releases/download/v2.1.7/geoweaver.jar java -jar geoweaver.jar ``` ## Complex Upgrade (Database Migration) -If you have been using Geoweaver for a while, it's important to ensure the safety of your database during the upgrade. Geoweaver relies on the H2 database as its default database. In some cases, the new version of Geoweaver may introduce changes to the H2 database library for security reasons. In such scenarios, users may need to migrate their Geoweaver database. +If you have used Geoweaver for a while, protect your database during major upgrades. Geoweaver’s default store is H2. When the bundled H2 library changes, you may need to migrate. -The official documentation for H2 database migration is available [here](https://www.h2database.com/html/migration-to-v2.html) +Official H2 migration notes: [Migration to H2 v2](https://www.h2database.com/html/migration-to-v2.html) -Here are the steps to migrate your Geoweaver database: +Steps: -1. Begin by downloading the H2 database JAR file of both the old version and the current version. Replace and respectively: +1. Download H2 JARs for the old and new versions (`` / ``): ```shell wget https://github.com/h2database/h2database/releases/download/version-/h2-.jar wget https://github.com/h2database/h2database/releases/download/version-/h2-.jar ``` -2. Export your data into an SQL script. Make sure to replace and with your specific information: +2. Export data (replace password): ```shell java -cp h2-.jar org.h2.tools.Script -url jdbc:h2:~/h2/gw -user geoweaver -script old_gw_db.sql -password ``` -3. Remove the old database files: +3. Remove old DB files (after a successful export): ```shell -rm ~/h2/* -f +rm -f ~/h2/* ``` -4. Import the data back and create a new Geoweaver database. Use the following command, replacing and with the appropriate values: +4. Import into a new DB: ```shell -java -cp h2-.jar org.h2.tools.RunScript -url jdbc:h2:~/h2/gw -user geoweaver -script old_gw_db.sql -password +java -cp h2-.jar org.h2.tools.RunScript -url jdbc:h2:~/h2/gw -user geoweaver -script old_gw_db.sql -password ``` -5. That's it! Download the new version of the Geoweaver JAR file and restart. Your Geoweaver installation should now work as usual: +5. Start the new Geoweaver jar with Java 17+: ```shell wget https://github.com/ESIPFed/Geoweaver/releases/download/latest/geoweaver.jar java -jar geoweaver.jar ``` -These steps should help you smoothly upgrade Geoweaver to the latest version while safeguarding your database. Any questions please feel free to report them [here](https://github.com/ESIPFed/Geoweaver/issues). +Questions: [GitHub Issues](https://github.com/ESIPFed/Geoweaver/issues). diff --git a/mkdocs.yml b/mkdocs.yml index a6f0835c4..693225d0f 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -1,4 +1,3 @@ - site_name: Geoweaver docs_dir: "docs" @@ -7,4 +6,19 @@ theme: name: readthedocs font: text: Roboto - logo: 'img/header.png' \ No newline at end of file + logo: 'img/header.png' + +nav: + - Home: index.md + - Installation: install.md + - Java 17 migration: java17-migration.md + - Upgrading: upgrade.md + - Hosts: host.md + - Processes: process.md + - Workflows: workflow.md + - Environment selection: environment-selection.md + - Database: database-configuration.md + - HTTP proxy: http-proxy.md + - Logging: logging.md + - Release process: release_upgrade.md + - Authors: authors.md