Skip to content

Commit c79c0fb

Browse files
committed
Deliver log in chunks
Each chunk is not taking longer than 30s to deliver (configurable via OSGi) This is due to the CDN not allowing streaming servlets (i.e. will always allow for it to finish before delivering it to the client). This closes #810
1 parent 5c5aa10 commit c79c0fb

File tree

5 files changed

+65
-25
lines changed

5 files changed

+65
-25
lines changed

accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/api/AcInstallationService.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,15 @@ public interface AcInstallationService {
4444
*/
4545
public boolean attachLogListener(String jobId, BiConsumer<InstallationLogLevel, String> listener, Consumer<Boolean> finishListener);
4646

47+
/**
48+
* Checks if the asynchronous installation job with the given ID is running.
49+
* @param jobId
50+
* @return {@code true} if the job with the given ID is running, {@code false} otherwise
51+
* @since 3.6.1
52+
*/
53+
public boolean isRunning(String jobId);
54+
55+
4756
/** Applies the full configuration as stored at the path configured at PID biz.netcentric.cq.tools.actool.impl.AcInstallationServiceImpl
4857
* to the repository.
4958
*

accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/api/package-info.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
@Version("3.1.0")
1+
@Version("3.2.0")
22
package biz.netcentric.cq.tools.actool.api;
33

44
/*-

accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/impl/AcInstallationServiceImpl.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,7 @@ public JobResult process(Job job) {
225225
@Override
226226
public boolean attachLogListener(String jobId, BiConsumer<InstallationLogLevel, String> messageListener, Consumer<Boolean> finishListener) {
227227
Job job = jobManager.getJobById(jobId);
228-
if (job == null) {
228+
if (!isRunning(jobId)) {
229229
// finished job or unknown job not to distinguish, as only failed jobs are kept in the history
230230
LOG.debug("No job found with id {}", jobId);
231231
return false;
@@ -241,6 +241,13 @@ public boolean attachLogListener(String jobId, BiConsumer<InstallationLogLevel,
241241
return true;
242242
}
243243

244+
245+
@Override
246+
public boolean isRunning(String jobId) {
247+
Job job = jobManager.getJobById(jobId);
248+
return job != null;
249+
}
250+
244251
@Override
245252
public InstallationLog apply() {
246253
return apply((String)null);

accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/ui/AcToolUiService.java

Lines changed: 41 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -123,12 +123,20 @@ public class AcToolUiService {
123123

124124
@AttributeDefinition(name="Write access", description="Principal names allowed to modify users/groups and permissions in the system via ACTool configuration files. Only leveraged for Touch UI but not for Web Console Plugin.")
125125
String[] writeAccessPrincipalNames() default { "administrators", "admin" };
126+
127+
@AttributeDefinition(name="Log stream request maximum runtime", description="Maximum time in milliseconds until the log stream request should be answered. Must be less than the timeout of the CDN (60 seconds for Fastly used by AEMaaCS).")
128+
long maxLogStreamRequestRuntimeInMs() default 30 * 1000; // 30 seconds
126129
}
127130

128131
private final Map<String, String> countryCodePerName;
129132

130133
private final Configuration config;
131134

135+
/**
136+
* Singleton message queue for the currently running asynchronous installation.
137+
*/
138+
private BlockingQueue<String> messages;
139+
132140
@Activate
133141
public AcToolUiService(Configuration config) {
134142
this.config = config;
@@ -289,6 +297,23 @@ protected void doPost(final HttpServletRequest req, final HttpServletResponse re
289297
}
290298
try {
291299
String jobId = acInstallationService.applyAsynchronously(builder.build());
300+
messages = new LinkedBlockingQueue<>();
301+
acInstallationService.attachLogListener(jobId, (level, message) -> {
302+
try {
303+
if (!reqParams.showLogVerbose && level == InstallationLogLevel.TRACE) {
304+
return;
305+
}
306+
messages.put(level + ": " + message);
307+
} catch (InterruptedException e) {
308+
Thread.currentThread().interrupt();
309+
}
310+
}, success -> {
311+
try {
312+
messages.put("FINISHED: " + success);
313+
} catch (InterruptedException e) {
314+
Thread.currentThread().interrupt();
315+
}
316+
});
292317
// return full URL to the server-sent event stream for this installation
293318
resp.setContentType("text/plain");
294319
String streamLogUrl = req.getRequestURI() + "/" + SUFFIX_STREAM_LOG + "?jobId=" + URLEncoder.encode(jobId, StandardCharsets.UTF_8.toString()) + "&" + PARAM_SHOW_LOG_VERBOSE + "=" + reqParams.showLogVerbose;
@@ -299,6 +324,7 @@ protected void doPost(final HttpServletRequest req, final HttpServletResponse re
299324
return;
300325
}
301326

327+
302328
}
303329

304330
/**
@@ -387,7 +413,9 @@ void downloadLog(HttpServletRequest req, final HttpServletResponse resp) throws
387413
}
388414
}
389415

390-
/** Server-sent events emitted for an asynchronous execution log
416+
/**
417+
* Server-sent events emitted for an asynchronous execution log. As the Fastly CDN used with AEM as a Cloud Service does not have <a href="https://docs.fastly.com/en/guides/streaming-miss">streaming</a>
418+
* enabled, this is delivering the events in chunks.
391419
* @throws IOException
392420
*
393421
* @see <a href="https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events">Server-sent events</a>
@@ -401,35 +429,20 @@ private void streamLog(HttpServletRequest req, HttpServletResponse resp) throws
401429
resp.setStatus(HttpServletResponse.SC_BAD_REQUEST);
402430
return;
403431
}
404-
final boolean isVerbose = Boolean.parseBoolean(req.getParameter(AcToolUiService.PARAM_SHOW_LOG_VERBOSE));
405-
BlockingQueue<String> messages = new LinkedBlockingQueue<>();
406-
boolean isActive = acInstallationService.attachLogListener(jobId, (level, message) -> {
407-
try {
408-
if (!isVerbose && level == InstallationLogLevel.TRACE) {
409-
return;
410-
}
411-
messages.put(level + ": " + message);
412-
} catch (InterruptedException e) {
413-
Thread.currentThread().interrupt();
414-
}
415-
}, success -> {
416-
try {
417-
messages.put("FINISHED: " + success);
418-
} catch (InterruptedException e) {
419-
Thread.currentThread().interrupt();
420-
}
421-
});
422-
if (!isActive) {
423-
LOG.debug("Haven't found job with id {}, probably already finished processing it", jobId);
432+
// is it fully consumed and no longer running?
433+
if (!acInstallationService.isRunning(jobId) && (messages == null || messages.isEmpty())) {
434+
LOG.debug("Asynchronous installation with id {} is not running anymore and no messages are available", jobId);
424435
resp.setStatus(HttpServletResponse.SC_NO_CONTENT);
425436
return;
426437
}
438+
long startTime = System.currentTimeMillis();
427439
try {
440+
boolean isActive = true;
428441
while (isActive) {
429442
String message = messages.poll(30, java.util.concurrent.TimeUnit.SECONDS);
430443
if (message == null) {
431-
// just sent keep-alives
432-
message = ".";
444+
message = "...";
445+
isActive = false;
433446
}
434447
if (message.startsWith("FINISHED: ")) {
435448
isActive = false;
@@ -438,6 +451,11 @@ private void streamLog(HttpServletRequest req, HttpServletResponse resp) throws
438451
resp.getWriter().println();
439452
resp.getWriter().flush();
440453
}
454+
// overall runtime above threshold
455+
if (System.currentTimeMillis() - startTime > config.maxLogStreamRequestRuntimeInMs()) {
456+
LOG.debug("Reached maximum runtime of {} ms for log stream request, finishing response", config.maxLogStreamRequestRuntimeInMs());
457+
isActive = false;
458+
}
441459
}
442460
} catch(InterruptedException e) {
443461
Thread.currentThread().interrupt();

pom.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,12 @@
244244
<groupId>biz.aQute.bnd</groupId>
245245
<artifactId>bnd-baseline-maven-plugin</artifactId>
246246
<version>${bnd.version}</version>
247+
<configuration>
248+
<diffignores>
249+
<!-- ignore Bundle-Version header for baseline, i.e. no enforcement of specific bundle versions -->
250+
<diffignore>Bundle-Version</diffignore>
251+
</diffignores>
252+
</configuration>
247253
</plugin>
248254
<plugin>
249255
<groupId>biz.aQute.bnd</groupId>

0 commit comments

Comments
 (0)