Skip to main content

Creating SectionBuilder Plugins

Plugins that implement report sections for ReportBuilder.

What is SectionBuilder

ReportBuilder holds multiple SectionBuilders as child actors and generates reports in the order they were added. Each SectionBuilder is responsible for one section.

ReportBuilder
├── WorkflowNameSection
├── WorkflowDescriptionSection
├── CheckResultsSection
├── JsonStateSection
├── TransitionHistorySection
├── GpuSummarySection
└── MyCustomSection ← Custom plugin

SectionBuilder Interface

package com.scivicslab.actoriac.report;

public interface SectionBuilder {

/**
* Generate section content.
* @return Section content string
*/
String generate();

/**
* Get section title.
* @return Title string (null to include title in generate())
*/
default String getTitle() { return null; }
}

Complete Implementation Example

POJO (SectionBuilder Implementation)

package com.example.plugin;

import com.scivicslab.actoriac.report.SectionBuilder;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.logging.Logger;

/**
* POJO for custom report section.
* Aggregates and displays command execution time per node.
*/
public class ExecutionTimeSection implements SectionBuilder {

private static final Logger logger = Logger.getLogger(ExecutionTimeSection.class.getName());

private Connection connection;
private long sessionId = -1;

// ========================================================================
// Setters
// ========================================================================

public void setConnection(Connection connection) {
this.connection = connection;
}

public void setSessionId(long sessionId) {
this.sessionId = sessionId;
}

// ========================================================================
// SectionBuilder Implementation
// ========================================================================

@Override
public String generate() {
if (connection == null || sessionId < 0) {
logger.warning("ExecutionTimeSection: connection or sessionId not set");
return "";
}

try {
return buildSection();
} catch (Exception e) {
logger.warning("ExecutionTimeSection: error: " + e.getMessage());
return "";
}
}

private String buildSection() throws Exception {
StringBuilder sb = new StringBuilder();
sb.append("[Execution Time Summary]\n");

String sql = "SELECT node_id, " +
"MIN(timestamp) as start_time, " +
"MAX(timestamp) as end_time, " +
"COUNT(*) as log_count " +
"FROM logs WHERE session_id = ? " +
"GROUP BY node_id ORDER BY node_id";

try (PreparedStatement ps = connection.prepareStatement(sql)) {
ps.setLong(1, sessionId);

try (ResultSet rs = ps.executeQuery()) {
while (rs.next()) {
String nodeId = rs.getString("node_id");
java.sql.Timestamp startTime = rs.getTimestamp("start_time");
java.sql.Timestamp endTime = rs.getTimestamp("end_time");
int logCount = rs.getInt("log_count");

long durationMs = endTime.getTime() - startTime.getTime();
String duration = formatDuration(durationMs);

sb.append(String.format("%s: %s (%d logs)%n",
nodeId, duration, logCount));
}
}
}

return sb.toString();
}

private String formatDuration(long ms) {
if (ms < 1000) {
return ms + "ms";
} else if (ms < 60000) {
return String.format("%.1fs", ms / 1000.0);
} else {
long minutes = ms / 60000;
long seconds = (ms % 60000) / 1000;
return String.format("%dm %ds", minutes, seconds);
}
}

@Override
public String getTitle() {
return null; // Title is included in generate()
}
}

IIAR

package com.example.plugin;

import com.scivicslab.actoriac.log.DistributedLogStore;
import com.scivicslab.pojoactor.core.Action;
import com.scivicslab.pojoactor.core.ActionResult;
import com.scivicslab.pojoactor.workflow.IIActorRef;
import com.scivicslab.pojoactor.workflow.IIActorSystem;

import java.sql.Connection;
import java.util.logging.Logger;

/**
* IIAR wrapper for ExecutionTimeSection.
*/
public class ExecutionTimeSectionIIAR extends IIActorRef<ExecutionTimeSection> {

private static final Logger logger = Logger.getLogger(ExecutionTimeSectionIIAR.class.getName());

public ExecutionTimeSectionIIAR(String actorName, IIActorSystem system) {
super(actorName, new ExecutionTimeSection(), system);
initializeFromSystem();
}

private void initializeFromSystem() {
DistributedLogStore logStore = DistributedLogStore.getInstance();
if (logStore != null) {
Connection conn = logStore.getConnection();
if (conn != null) {
object.setConnection(conn);
}
}

long sessionId = getSessionIdFromNodeGroup();
if (sessionId >= 0) {
object.setSessionId(sessionId);
}
}

private long getSessionIdFromNodeGroup() {
if (actorSystem == null) return -1;

IIActorRef<?> nodeGroup = ((IIActorSystem) actorSystem).getIIActor("nodeGroup");
if (nodeGroup == null) return -1;

ActionResult result = nodeGroup.callByActionName("getSessionId", "");
if (result.isSuccess()) {
try {
return Long.parseLong(result.getResult());
} catch (NumberFormatException e) {
// ignore
}
}
return -1;
}

// ========================================================================
// Standard Actions for SectionBuilder
// ========================================================================

@Action("generate")
public ActionResult generate(String args) {
String content = object.generate();
return new ActionResult(true, content);
}

@Action("getTitle")
public ActionResult getTitle(String args) {
String title = object.getTitle();
return new ActionResult(true, title != null ? title : "");
}
}

Usage in ReportBuilder

name: report-with-custom-section
description: Report with custom section

steps:
- states: ["0", "1"]
note: Load plugin and create ReportBuilder
actions:
# Load plugin
- actor: loader
method: loadJar
arguments: ["plugins/execution-time-section-1.0.0.jar"]

# Create ReportBuilder
- actor: loader
method: createChild
arguments: ["ROOT", "reportBuilder", "com.scivicslab.actoriac.report.ReportBuilderIIAR"]

# Add standard sections
- actor: loader
method: createChild
arguments: ["reportBuilder", "wfName", "com.scivicslab.actoriac.report.sections.basic.WorkflowNameSectionIIAR"]
- actor: loader
method: createChild
arguments: ["reportBuilder", "checkResults", "com.scivicslab.actoriac.report.sections.basic.CheckResultsSectionIIAR"]

# Add custom section
- actor: loader
method: createChild
arguments: ["reportBuilder", "execTime", "com.example.plugin.ExecutionTimeSectionIIAR"]

- states: ["1", "2"]
note: Run workflow
actions:
- actor: nodeGroup
method: apply
arguments:
actor: "node-*"
method: runWorkflow
arguments: ["some-workflow.yaml"]

- states: ["2", "end"]
note: Generate report
actions:
- actor: reportBuilder
method: report

Output Example

================================================================================
WORKFLOW REPORT
================================================================================

[Workflow Name]
report-with-custom-section

[Check Results]
node-192.168.5.13: [OK] Deployment successful
node-192.168.5.14: [OK] Deployment successful

[Execution Time Summary]
node-192.168.5.13: 12.3s (45 logs)
node-192.168.5.14: 15.7s (52 logs)
nodeGroup: 18.2s (23 logs)

================================================================================

Passing Parameters via Actor Name

You can encode options in the actor name:

public ExecutionTimeSectionIIAR(String actorName, IIActorSystem system) {
super(actorName, new ExecutionTimeSection(), system);
parseActorName(actorName);
initializeFromSystem();
}

private void parseActorName(String actorName) {
// Format: "execTime:detailed" or "execTime:summary"
String[] parts = actorName.split(":", 2);
if (parts.length >= 2) {
object.setDetailLevel(parts[1]);
}
}

Workflow:

# Detailed mode
- actor: loader
method: createChild
arguments: ["reportBuilder", "execTime:detailed", "...ExecutionTimeSectionIIAR"]

# Summary mode
- actor: loader
method: createChild
arguments: ["reportBuilder", "execTime:summary", "...ExecutionTimeSectionIIAR"]

Reference Standard SectionBuilders

For implementation reference, check the source code of standard SectionBuilders:

ClassDescriptionReference Point
CheckResultsSectionCollects % notation linesPattern extraction from logs
JsonStateSectionOutputs JsonState as YAMLGetting actor references
TransitionHistorySectionTransition historyComplex SQL aggregation
GpuSummarySectionGPU info summaryParsing log messages

Package: com.scivicslab.actoriac.report.sections.basic