Skip to content

Commit 6d4d702

Browse files
committed
Stabilizing DetachAttachITest
- Refactoring to fix occasional race condition seen on GH CI on Windows - Added toStrings - Added logs - API cleanup - Few javadocs - Default jobs.xml provider pushed outside JobManagerService, which generally manages all jobfiles. - JobFileScanner moved to JobManagerService method - not used anywhere else - JobPersistenceService is responsible for the I/O to the file - Added client counting to the AttachCommand to prevent race conditions Signed-off-by: David Matějček <[email protected]>
1 parent 7b35590 commit 6d4d702

File tree

24 files changed

+620
-657
lines changed

24 files changed

+620
-657
lines changed

appserver/tests/admin/tests/src/test/java/org/glassfish/main/admin/test/progress/DetachAttachITest.java

Lines changed: 23 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
import static org.hamcrest.Matchers.greaterThanOrEqualTo;
4242
import static org.hamcrest.Matchers.not;
4343
import static org.hamcrest.Matchers.stringContainsInOrder;
44+
import static org.junit.jupiter.api.Assertions.assertAll;
4445
import static org.junit.jupiter.api.Assertions.assertEquals;
4546
import static org.junit.jupiter.api.Assertions.assertFalse;
4647
import static org.junit.jupiter.api.Assertions.assertNotNull;
@@ -95,28 +96,32 @@ public void commandWithProgressStatus() throws Exception {
9596

9697
@Test
9798
public void detachOnesAttachMulti() throws Exception {
98-
ExecutorService pool = Executors.newCachedThreadPool(r -> {
99-
Thread result = new Thread(r);
100-
result.setDaemon(true);
101-
return result;
99+
final ExecutorService pool = Executors.newCachedThreadPool(r -> {
100+
Thread thread = new Thread(r);
101+
thread.setDaemon(true);
102+
return thread;
102103
});
103-
final DetachedTerseAsadminResult result = ASADMIN.execDetached("progress-custom", "8x1");
104-
assertThat(result, asadminOK());
105-
assertNotNull(result.getJobId(), "id");
104+
final DetachedTerseAsadminResult jobIdResult = ASADMIN.execDetached("progress-custom", "8x1");
105+
assertThat(jobIdResult, asadminOK());
106+
assertNotNull(jobIdResult.getJobId(), "id");
106107
final int attachCount = 3;
107-
Collection<Callable<AsadminResult>> attaches = new ArrayList<>(attachCount);
108+
final Collection<Callable<AsadminResult>> attaches = new ArrayList<>(attachCount);
108109
for (int i = 0; i < attachCount; i++) {
109-
attaches.add(() -> ASADMIN.exec("attach", result.getJobId()));
110+
attaches.add(() -> ASADMIN.exec("attach", jobIdResult.getJobId()));
110111
}
111-
List<Future<AsadminResult>> results = pool.invokeAll(attaches);
112-
for (Future<AsadminResult> fRes : results) {
113-
AsadminResult res = fRes.get();
114-
assertThat(res, asadminOK());
115-
assertTrue(res.getStdOut().contains("progress-custom"));
116-
List<ProgressMessage> prgs = ProgressMessage.grepProgressMessages(res.getStdOut());
117-
assertFalse(prgs.isEmpty());
118-
assertThat(prgs.get(0).getValue(), greaterThanOrEqualTo(0));
119-
assertEquals(100, prgs.get(prgs.size() - 1).getValue());
112+
final List<Future<AsadminResult>> futureResults = pool.invokeAll(attaches);
113+
for (Future<AsadminResult> futureResult : futureResults) {
114+
final AsadminResult result = futureResult.get();
115+
final List<ProgressMessage> prgs = ProgressMessage.grepProgressMessages(result.getStdOut());
116+
assertAll(
117+
() -> assertThat(result, asadminOK()),
118+
() -> assertTrue(result.getStdOut().contains("progress-custom")),
119+
() -> assertFalse(prgs.isEmpty(), "progress messages empty")
120+
);
121+
assertAll(
122+
() -> assertThat(prgs.get(0).getValue(), greaterThanOrEqualTo(0)),
123+
() -> assertEquals(100, prgs.get(prgs.size() - 1).getValue())
124+
);
120125
}
121126
}
122127
}

nucleus/admin/rest/rest-service/src/main/java/org/glassfish/admin/rest/utils/SseCommandHelper.java

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
/*
2+
* Copyright (c) 2025 Contributors to the Eclipse Foundation
23
* Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
34
*
45
* This program and the accompanying materials are made available under the
@@ -17,7 +18,6 @@
1718
package org.glassfish.admin.rest.utils;
1819

1920
import com.sun.enterprise.admin.remote.AdminCommandStateImpl;
20-
import com.sun.enterprise.util.LocalStringManagerImpl;
2121
import com.sun.enterprise.v3.admin.JobManagerService;
2222
import com.sun.enterprise.v3.common.PropsFileActionReporter;
2323

@@ -27,22 +27,24 @@
2727
import java.util.logging.Level;
2828

2929
import org.glassfish.admin.rest.RestLogging;
30-
import org.glassfish.admin.rest.resources.admin.CommandResource;
3130
import org.glassfish.api.ActionReport;
3231
import org.glassfish.api.admin.AdminCommandEventBroker;
32+
import org.glassfish.api.admin.AdminCommandEventBroker.AdminCommandListener;
3333
import org.glassfish.api.admin.AdminCommandState;
3434
import org.glassfish.api.admin.CommandRunner;
3535
import org.glassfish.api.admin.CommandRunner.CommandInvocation;
3636
import org.glassfish.internal.api.Globals;
3737
import org.glassfish.jersey.media.sse.EventOutput;
3838
import org.glassfish.jersey.media.sse.OutboundEvent;
3939

40+
import static org.glassfish.api.admin.AdminCommandState.EVENT_STATE_CHANGED;
41+
4042
/**
4143
* Provides bridge between CommandInvocation and ReST Response for SSE. Create it and call execute.
4244
*
4345
* @author martinmares
4446
*/
45-
public class SseCommandHelper implements Runnable, AdminCommandEventBroker.AdminCommandListener {
47+
public class SseCommandHelper implements Runnable, AdminCommandListener {
4648

4749
/**
4850
* If implementation of this interface is registered then it's process() method is used to convert ActionReport before
@@ -58,8 +60,6 @@ public static interface ActionReportProcessor {
5860

5961
}
6062

61-
private final static LocalStringManagerImpl strings = new LocalStringManagerImpl(CommandResource.class);
62-
6363
private final CommandRunner.CommandInvocation commandInvocation;
6464
private final ActionReportProcessor processor;
6565
private final EventOutput eventOuptut = new EventOutput();
@@ -80,7 +80,7 @@ public void run() {
8080
actionReport.setFailureCause(thr);
8181
actionReport.setActionExitCode(ActionReport.ExitCode.FAILURE);
8282
AdminCommandState acs = new AdminCommandStateImpl(AdminCommandState.State.COMPLETED, actionReport, true, "unknown");
83-
onAdminCommandEvent(AdminCommandStateImpl.EVENT_STATE_CHANGED, acs);
83+
onAdminCommandEvent(EVENT_STATE_CHANGED, acs);
8484
} finally {
8585
try {
8686
eventOuptut.close();
@@ -97,7 +97,7 @@ private void unregister() {
9797
}
9898

9999
private Object process(final String name, Object event) {
100-
if (processor != null && AdminCommandStateImpl.EVENT_STATE_CHANGED.equals(name)) {
100+
if (processor != null && EVENT_STATE_CHANGED.equals(name)) {
101101
AdminCommandState acs = (AdminCommandState) event;
102102
ActionReport report = processor.process(acs.getActionReport(), eventOuptut);
103103
event = new AdminCommandStateImpl(acs.getState(), report, acs.isOutboundPayloadEmpty(), acs.getId());
@@ -122,8 +122,8 @@ public void onAdminCommandEvent(final String name, Object event) {
122122
unregister();
123123
return;
124124
}
125-
if ((event instanceof Number) || (event instanceof CharSequence) || (event instanceof Boolean)) {
126-
event = String.valueOf(event);
125+
if (event instanceof Number || event instanceof CharSequence || event instanceof Boolean) {
126+
event = event.toString();
127127
}
128128
event = process(name, event);
129129
OutboundEvent outEvent = new OutboundEvent.Builder().name(name)
Lines changed: 41 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
/*
2+
* Copyright (c) 2025 Contributors to the Eclipse Foundation
23
* Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
34
*
45
* This program and the accompanying materials are made available under the
@@ -17,6 +18,7 @@
1718
package com.sun.enterprise.admin.remote;
1819

1920
import java.io.Serializable;
21+
import java.util.Objects;
2022

2123
import org.glassfish.api.ActionReport;
2224
import org.glassfish.api.admin.AdminCommandState;
@@ -30,54 +32,66 @@ public class AdminCommandStateImpl implements AdminCommandState, Serializable {
3032

3133
private static final long serialVersionUID = 1L;
3234

33-
protected State state = State.PREPARED;
34-
protected ActionReport actionReport;
35-
private boolean payloadIsEmpty;
36-
protected String id;
37-
38-
public AdminCommandStateImpl(final State state, final ActionReport actionReport, final boolean payloadIsEmpty, final String id) {
39-
this.state = state;
40-
this.actionReport = actionReport;
41-
this.payloadIsEmpty = payloadIsEmpty;
42-
this.id = id;
43-
}
35+
private final String id;
36+
private final boolean payloadIsEmpty;
37+
private ActionReport actionReport;
38+
private volatile State state;
4439

4540
public AdminCommandStateImpl(String id) {
4641
this(State.PREPARED, null, true, id);
4742
}
4843

44+
public AdminCommandStateImpl(final State state, final ActionReport actionReport, final boolean payloadIsEmpty, final String id) {
45+
this.id = id;
46+
this.payloadIsEmpty = payloadIsEmpty;
47+
this.actionReport = actionReport;
48+
this.state = state;
49+
}
50+
4951
@Override
50-
public State getState() {
51-
return this.state;
52+
public final String getId() {
53+
return this.id;
5254
}
5355

5456
@Override
55-
public void complete(ActionReport actionReport) {
56-
this.actionReport = actionReport;
57-
if (getState().equals(State.REVERTING)) {
58-
setState(State.REVERTED);
59-
} else {
60-
setState(State.COMPLETED);
61-
}
57+
public boolean isOutboundPayloadEmpty() {
58+
return this.payloadIsEmpty;
6259
}
6360

6461
@Override
65-
public ActionReport getActionReport() {
62+
public final ActionReport getActionReport() {
6663
return this.actionReport;
6764
}
6865

6966
@Override
70-
public boolean isOutboundPayloadEmpty() {
71-
return this.payloadIsEmpty;
67+
public final State getState() {
68+
return this.state;
7269
}
7370

74-
@Override
75-
public String getId() {
76-
return this.id;
71+
/**
72+
* Sets the state and the action report.
73+
*
74+
* @param state must not be null.
75+
* @param report must not be null.
76+
*/
77+
protected final void setState(State state, ActionReport report) {
78+
// The order matters - setStatus is overridden by some children,
79+
// following actions depend on the report!
80+
this.actionReport = Objects.requireNonNull(report, "report");
81+
setState(state);
7782
}
7883

84+
/**
85+
* Sets the state.
86+
*
87+
* @param state must not be null
88+
*/
7989
protected void setState(State state) {
80-
this.state = state;
90+
this.state = Objects.requireNonNull(state, "state");
8191
}
8292

93+
@Override
94+
public String toString() {
95+
return super.toString() + "[id=" + id + ", state=" + state + ", report=" + actionReport + "]";
96+
}
8397
}

nucleus/common/glassfish-api/src/main/java/org/glassfish/api/ActionReport.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2024 Contributors to the Eclipse Foundation.
2+
* Copyright (c) 2024, 2025 Contributors to the Eclipse Foundation.
33
* Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
44
*
55
* This program and the accompanying materials are made available under the
@@ -294,4 +294,10 @@ public String findProperty(String key) {
294294
}
295295
return null;
296296
}
297+
298+
299+
@Override
300+
public String toString() {
301+
return super.toString() + "[exitCode=" + getActionExitCode() + ", message=" + getMessage() + "]";
302+
}
297303
}

nucleus/common/glassfish-api/src/main/java/org/glassfish/api/admin/AdminCommandState.java

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
/*
2+
* Copyright (c) 2025 Contributors to the Eclipse Foundation
23
* Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
34
*
45
* This program and the accompanying materials are made available under the
@@ -33,14 +34,6 @@ public enum State {
3334

3435
State getState();
3536

36-
/**
37-
* Completes whole progress and records result
38-
*
39-
* @param actionReport result message
40-
*
41-
*/
42-
void complete(ActionReport actionReport);
43-
4437
ActionReport getActionReport();
4538

4639
/**

nucleus/common/glassfish-api/src/main/java/org/glassfish/api/admin/JobManager.java

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
/*
2+
* Copyright (c) 2025 Contributors to the Eclipse Foundation
23
* Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
34
*
45
* This program and the accompanying materials are made available under the
@@ -21,6 +22,7 @@
2122
import java.io.Serializable;
2223
import java.util.Iterator;
2324

25+
import org.glassfish.api.admin.progress.JobInfo;
2426
import org.glassfish.api.admin.progress.JobInfos;
2527
import org.jvnet.hk2.annotations.Contract;
2628

@@ -92,7 +94,7 @@ public AdminCommandContext getContext() {
9294
* This method is used to get a job by its id
9395
*
9496
* @param id The id to look up the job in the job registry
95-
* @return the Job
97+
* @return the Job or null
9698
*/
9799
Job get(String id);
98100

@@ -106,7 +108,7 @@ public AdminCommandContext getContext() {
106108
/**
107109
* This will get the list of jobs from the job registry which have completed
108110
*
109-
* @return the details of all completed jobs using JobInfos
111+
* @return the details of all completed jobs using JobInfos. Never null.
110112
*/
111113
JobInfos getCompletedJobs(File jobs);
112114

@@ -116,22 +118,15 @@ public AdminCommandContext getContext() {
116118
* @param id the completed Job whose id needs to be looked up
117119
* @return the completed Job
118120
*/
119-
Object getCompletedJobForId(String id);
121+
JobInfo getCompletedJobForId(String id, File jobsFile);
120122

121123
/**
122-
* This is used to purge a completed job whose id is provided
124+
* This is used to purge a completed job.
123125
*
124-
* @param id the id of the Job which needs to be purged
126+
* @param job the info about Job
125127
* @return the new list of completed jobs
126128
*/
127-
Object purgeCompletedJobForId(String id);
128-
129-
/**
130-
* This is used to get the jobs file for a job
131-
*
132-
* @return the location of the job file
133-
*/
134-
File getJobsFile();
129+
JobInfos purgeCompletedJobForId(JobInfo job);
135130

136131
/**
137132
* Stores current command state.

nucleus/common/glassfish-api/src/main/java/org/glassfish/api/admin/progress/JobInfo.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
/*
2+
* Copyright (c) 2025 Contributors to the Eclipse Foundation
23
* Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
34
*
45
* This program and the accompanying materials are made available under the
@@ -73,4 +74,8 @@ public void setJobsFile(File jobsFile) {
7374
this.jobFile = jobsFile;
7475
}
7576

77+
@Override
78+
public String toString() {
79+
return "Job[id=" + jobId + ", name=" + jobName + ", exitCode=" + exitCode + "]";
80+
}
7681
}

nucleus/common/glassfish-api/src/main/java/org/glassfish/api/admin/progress/JobInfos.java

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
/*
2+
* Copyright (c) 2025 Contributors to the Eclipse Foundation
23
* Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
34
*
45
* This program and the accompanying materials are made available under the
@@ -31,16 +32,24 @@
3132
public class JobInfos {
3233
private List<JobInfo> jobInfoList;
3334

35+
/**
36+
* @return the internal list, never null.
37+
*/
3438
@XmlElement(name = "job")
3539
public List<JobInfo> getJobInfoList() {
36-
3740
return jobInfoList;
3841
}
3942

43+
/**
44+
* @param jobInfoList can be null, then creates an empty {@link ArrayList}.
45+
*/
4046
public void setJobInfoList(List<JobInfo> jobInfoList) {
41-
this.jobInfoList = jobInfoList;
47+
this.jobInfoList = jobInfoList == null ? new ArrayList<>() : jobInfoList;
4248
}
4349

50+
/**
51+
* Creates an empty list.
52+
*/
4453
public JobInfos() {
4554
jobInfoList = new ArrayList<>();
4655
}

0 commit comments

Comments
 (0)