Skip to content

Commit 8278a26

Browse files
authored
Update StartLabKey and StopLabKey tasks (#235)
- Update `stopLabKey` task to work without Spring's shutdown endpoint - Conditionally enable Java heap dumps in `StartLabKey` task
1 parent deb0da1 commit 8278a26

6 files changed

Lines changed: 60 additions & 28 deletions

File tree

README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,13 @@ on how to do that, including how to develop and test locally and the versioning
1313

1414
## Release Notes
1515

16+
### 7.1.0
17+
*Released*: 17 October 2025
18+
(Earliest compatible LabKeyversion: 25.10)
19+
- [Issue 53244](https://www.labkey.org/home/Developer/issues/issues-details.view?issueId=53244): Update `stopLabKey` task to work without Spring's shutdown endpoint
20+
- [Issue 53792](https://www.labkey.org/home/Developer/issues/issues-details.view?issueId=53792): Conditionally enable Java heap dumps in `StartLabKey` task
21+
- Fix output destination of `application.properties` for `DeployApp` task
22+
1623
### 7.0.0
1724
*Released*: 22 September 2025
1825
(Earliest compatible LabKey version: 25.10)
@@ -36,6 +43,7 @@ on how to do that, including how to develop and test locally and the versioning
3643
- Update `PropertiesUtils` to remove code for supporting `labkey.xml`
3744
- Relocate the `.restartTriggerFile` to prevent creation of empty `build/deploy/modules` directory for embedded distribution deployment
3845
- Fix `bootstrap` task so it will update `application.properties`
46+
- Remove `description` property from `DistributionExtension`
3947

4048
### 6.3.0
4149
*Released*: 3 July 2025

build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ dependencies {
4343
}
4444

4545
group = 'org.labkey.build'
46-
project.version = "7.1.0-SNAPSHOT"
46+
project.version = "7.2.0-SNAPSHOT"
4747

4848
gradlePlugin {
4949
plugins {

src/main/groovy/org/labkey/gradle/plugin/TestRunner.groovy

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,7 @@ class TestRunner extends UiTest
169169
destdir: BuildUtils.getBuildDirFile(project,"classes/java/uiTest/").getPath(),
170170
source: project.sourceCompatibility,
171171
target: project.targetCompatibility,
172+
encoding: "UTF-8",
172173
classpath: project.configurations.uiTestRuntimeClasspath.asPath,
173174
{
174175
project.sourceSets.uiTest.java.srcDirs.each {

src/main/groovy/org/labkey/gradle/task/SetUpProperties.groovy

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,8 @@ abstract class SetUpProperties extends TeamCityPropertiesTask
6363
final abstract Property<String> pathToServer = project.objects.property(String).convention(project.rootDir.getAbsolutePath().replaceAll("\\\\", "/"))
6464

6565
@Input
66-
final abstract Property<String> embeddedDir = project.objects.property(String).convention(BuildUtils.getEmbeddedConfigPath(project))
66+
final abstract Property<String> embeddedConfigDir = project.objects.property(String).convention(BuildUtils.getEmbeddedConfigPath(project))
67+
6768
@InputDirectory
6869
File configsDir = new File(BuildUtils.getConfigsProject(project).projectDir, "configs")
6970

@@ -87,7 +88,7 @@ abstract class SetUpProperties extends TeamCityPropertiesTask
8788

8889
fs.copy({ CopySpec copy ->
8990
copy.from configsDir
90-
copy.into embeddedDir.get()
91+
copy.into embeddedConfigDir.get()
9192
copy.include "application.properties"
9293
copy.setDuplicatesStrategy(DuplicatesStrategy.INCLUDE)
9394
copy.filter({ String line ->

src/main/groovy/org/labkey/gradle/task/StartLabKey.groovy

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,8 +110,14 @@ abstract class StartLabKey extends TeamCityPropertiesTask
110110
optsList.add("-DsequencePipelineEnabled=${TeamCityExtension.getTeamCityProperty(project, "sequencePipelineEnabled", false)}".toString())
111111
}
112112

113-
if (project.hasProperty("extraCatalinaOpts"))
114-
optsList.addAll(((String) project.property("extraCatalinaOpts")).split("\\s+"))
113+
if (TeamCityExtension.getTeamCityProperty(project, "labkey.heapDumpOnOutOfMemoryError", TeamCityExtension.isOnTeamCity(project)))
114+
{
115+
optsList.add("-XX:+HeapDumpOnOutOfMemoryError");
116+
}
117+
118+
String extraCatalinaOpts = TeamCityExtension.getTeamCityProperty(project, "extraCatalinaOpts", "")
119+
if (!extraCatalinaOpts.isEmpty())
120+
optsList.addAll(extraCatalinaOpts.split("\\s+"))
115121

116122
return optsList.stream()
117123
.filter({String opt -> return !StringUtils.isEmpty(opt)})

src/main/groovy/org/labkey/gradle/task/StopLabKey.groovy

Lines changed: 39 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -16,40 +16,56 @@
1616
package org.labkey.gradle.task
1717

1818
import org.gradle.api.DefaultTask
19-
import org.gradle.api.file.RegularFileProperty
20-
import org.gradle.api.provider.Property
21-
import org.gradle.api.tasks.Input
2219
import org.gradle.api.tasks.InputFile
20+
import org.gradle.api.tasks.Optional
2321
import org.gradle.api.tasks.TaskAction
24-
import org.labkey.gradle.util.PropertiesUtils
25-
import org.labkey.gradle.util.BuildUtils
22+
import org.labkey.gradle.plugin.extension.ServerDeployExtension
23+
24+
import java.nio.charset.StandardCharsets
25+
import java.nio.file.Files
26+
import java.util.concurrent.TimeUnit
2627

2728
/**
2829
* Task for stopping a running LabKey instance
2930
*/
3031
class StopLabKey extends DefaultTask
3132
{
32-
@InputFile
33-
final abstract RegularFileProperty propertiesFile = project.objects.fileProperty().fileValue(
34-
BuildUtils.getApplicationPropertiesFile(project)
35-
)
3633

37-
@Input
38-
final abstract Property<Boolean> useSsl = project.objects.property(Boolean).convention(project.hasProperty("useSsl"))
34+
@InputFile @Optional
35+
final abstract File pidFile = ServerDeployExtension.getEmbeddedDir(project).file("labkey.pid").asFile
36+
.with { it.exists() ? it : null } // "Optional" means that the property may be null, not refer to something nonexistent
3937

4038
@TaskAction
4139
void action()
4240
{
43-
def applicationProperties = PropertiesUtils.getApplicationProperties(propertiesFile.get().asFile)
44-
def port = applicationProperties.getProperty("management.server.port", applicationProperties.getProperty("server.port"))
45-
def endpoint = "${useSsl.get() ? "https" : "http"}://localhost:$port/actuator/shutdown"
46-
def command = "curl -X POST $endpoint"
47-
this.logger.info("Sending command to $endpoint")
48-
def proc = command.execute()
49-
proc.waitFor()
50-
if (proc.exitValue() != 0)
51-
this.logger.warn("Shutdown command exited with non-zero status ${proc.exitValue()}.")
52-
else
53-
this.logger.quiet("Shutdown successful")
41+
if (pidFile != null && pidFile.exists()) {
42+
String pidStr = new String(Files.readAllBytes(pidFile.toPath()), StandardCharsets.UTF_8).trim()
43+
Integer pid = Integer.parseInt(pidStr)
44+
stopLabKeyByPid(pid)
45+
} else {
46+
logger.info("LabKey doesn't appear to be running in this enlistment. PID file not found")
47+
}
48+
49+
}
50+
51+
private void stopLabKeyByPid(long pid)
52+
{
53+
ProcessHandle.of(pid).ifPresentOrElse({ processHandle ->
54+
if (processHandle.destroy()) {
55+
// Wait up to 30 seconds for the process to terminate
56+
boolean isTerminated = processHandle.onExit().orTimeout(30, TimeUnit.SECONDS)
57+
.thenApply({ handle -> true })
58+
.exceptionally({ throwable -> false })
59+
.get()
60+
61+
if (isTerminated) {
62+
logger.info("Successfully terminated LabKey process with PID: {}", pid)
63+
} else {
64+
logger.warn("Process with PID {} did not terminate within timeout period", pid)
65+
}
66+
} else {
67+
logger.warn("Failed to initiate termination of LabKey process with PID: {}", pid)
68+
}
69+
}, { () -> logger.warn("No process found with PID {}", pid)} )
5470
}
55-
}
71+
}

0 commit comments

Comments
 (0)