001/* 002 * Copyright 2025 devteam@scivicslab.com 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, 011 * software distributed under the License is distributed on an 012 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 013 * either express or implied. See the License for the 014 * specific language governing permissions and limitations 015 * under the License. 016 */ 017 018package com.scivicslab.actoriac.report; 019 020import com.scivicslab.pojoactor.core.Action; 021import com.scivicslab.pojoactor.core.ActionResult; 022import com.scivicslab.pojoactor.core.JsonState; 023import com.scivicslab.pojoactor.workflow.IIActorRef; 024import com.scivicslab.pojoactor.workflow.IIActorSystem; 025 026import org.json.JSONObject; 027import org.yaml.snakeyaml.Yaml; 028 029import java.io.InputStream; 030import java.nio.file.Files; 031import java.nio.file.Path; 032import java.nio.file.Paths; 033import java.util.Map; 034import java.util.logging.Logger; 035 036/** 037 * Actor for building and outputting workflow reports. 038 * 039 * <p>This actor wraps a {@link ReportBuilder} POJO and provides workflow-callable 040 * actions via {@code @Action} annotations. It handles the integration with the 041 * actor system, database, and output multiplexer.</p> 042 * 043 * <h2>Actions:</h2> 044 * <ul> 045 * <li>{@code addWorkflowInfo} - Add workflow metadata section</li> 046 * <li>{@code addJsonStateSection} - Add actor's JsonState as YAML</li> 047 * <li>{@code report} - Build and output the report</li> 048 * </ul> 049 * 050 * <p>Transition履歴の詳細表示は{@code TransitionViewerPlugin}で行う。</p> 051 * 052 * @author devteam@scivicslab.com 053 * @since 2.15.0 054 */ 055public class ReportBuilderIIAR extends IIActorRef<ReportBuilder> { 056 057 private static final String CLASS_NAME = ReportBuilderIIAR.class.getName(); 058 private static final Logger logger = Logger.getLogger(CLASS_NAME); 059 060 /** 061 * Constructs a new ReportBuilderIIAR with a new POJO instance. 062 * 063 * <p>Required by {@code loader.createChild} for dynamic instantiation.</p> 064 * 065 * @param actorName the actor name 066 * @param system the actor system 067 */ 068 public ReportBuilderIIAR(String actorName, IIActorSystem system) { 069 super(actorName, new ReportBuilder(), system); 070 // POJOにsystemとselfRefを設定(ReportBuilder.build()が子アクターを取得するため) 071 this.object.setActorSystem(system); 072 this.object.setIIActorRef(this); 073 } 074 075 /** 076 * Constructs a new ReportBuilderIIAR. 077 * 078 * @param name the actor name 079 * @param builder the ReportBuilder POJO to wrap 080 */ 081 public ReportBuilderIIAR(String name, ReportBuilder builder) { 082 super(name, builder); 083 } 084 085 /** 086 * Constructs a new ReportBuilderIIAR with actor system. 087 * 088 * @param name the actor name 089 * @param builder the ReportBuilder POJO to wrap 090 * @param system the actor system 091 */ 092 public ReportBuilderIIAR(String name, ReportBuilder builder, IIActorSystem system) { 093 super(name, builder, system); 094 } 095 096 // ======================================================================== 097 // Actions 098 // ======================================================================== 099 100 /** 101 * Adds workflow info section. 102 * 103 * <p>Gets workflow path from nodeGroup, reads the YAML file to extract 104 * name and description, and adds a WorkflowInfoSection to the report.</p> 105 * 106 * @param args unused 107 * @return ActionResult indicating success or failure 108 */ 109 @Action("addWorkflowInfo") 110 public ActionResult addWorkflowInfo(String args) { 111 logger.info("ReportBuilderIIAR.addWorkflowInfo"); 112 113 String workflowPath = getWorkflowPathFromNodeGroup(); 114 if (workflowPath == null) { 115 return new ActionResult(false, "Could not get workflow path from nodeGroup"); 116 } 117 118 String name = null; 119 String description = null; 120 121 // Try to read workflow YAML for name and description 122 try { 123 Path path = Paths.get(workflowPath); 124 if (!Files.exists(path)) { 125 path = Paths.get(System.getProperty("user.dir"), workflowPath); 126 } 127 128 if (Files.exists(path)) { 129 try (InputStream is = Files.newInputStream(path)) { 130 Yaml yaml = new Yaml(); 131 Map<String, Object> data = yaml.load(is); 132 if (data != null) { 133 name = (String) data.get("name"); 134 Object descObj = data.get("description"); 135 description = descObj != null ? descObj.toString().trim() : null; 136 } 137 } 138 } 139 } catch (Exception e) { 140 logger.warning("ReportBuilderIIAR.addWorkflowInfo: Could not read workflow file: " + e.getMessage()); 141 } 142 143 this.object.addSection(new WorkflowInfoSection(workflowPath, name, description)); 144 return new ActionResult(true, "Workflow info section added"); 145 } 146 147 /** 148 * Adds JsonState section for specified actor. 149 * 150 * <p>Arguments (JSON):</p> 151 * <ul> 152 * <li>{@code actor} - Actor name (required)</li> 153 * <li>{@code path} - JsonState path filter (optional)</li> 154 * </ul> 155 * 156 * @param args JSON arguments 157 * @return ActionResult indicating success or failure 158 */ 159 @Action("addJsonStateSection") 160 public ActionResult addJsonStateSection(String args) { 161 logger.info("ReportBuilderIIAR.addJsonStateSection: args=" + args); 162 163 String actorName; 164 String path = ""; 165 166 try { 167 JSONObject json = new JSONObject(args); 168 actorName = json.getString("actor"); 169 path = json.optString("path", ""); 170 } catch (Exception e) { 171 return new ActionResult(false, "Invalid arguments: " + e.getMessage()); 172 } 173 174 if (system() == null) { 175 return new ActionResult(false, "ActorSystem not available"); 176 } 177 178 IIActorRef<?> targetActor = ((IIActorSystem) system()).getIIActor(actorName); 179 if (targetActor == null) { 180 return new ActionResult(false, "Actor not found: " + actorName); 181 } 182 183 JsonState jsonState = targetActor.json(); 184 if (jsonState == null) { 185 return new ActionResult(false, "Actor has no JsonState: " + actorName); 186 } 187 188 String yamlContent = jsonState.toStringOfYaml(path); 189 this.object.addSection(new JsonStateSection(actorName, yamlContent)); 190 191 return new ActionResult(true, "JsonState section added for " + actorName); 192 } 193 194 /** 195 * Builds and outputs the report. 196 * 197 * @param args unused 198 * @return ActionResult with the report content 199 */ 200 @Action("report") 201 public ActionResult report(String args) { 202 logger.info("ReportBuilderIIAR.report"); 203 204 String reportContent = this.object.build(); 205 reportToMultiplexer(reportContent); 206 207 return new ActionResult(true, reportContent); 208 } 209 210 // ======================================================================== 211 // Helper Methods 212 // ======================================================================== 213 214 /** 215 * Gets workflow path from nodeGroup actor. 216 */ 217 private String getWorkflowPathFromNodeGroup() { 218 if (system() == null) return null; 219 IIActorSystem iiSystem = (IIActorSystem) system(); 220 221 IIActorRef<?> nodeGroup = iiSystem.getIIActor("nodeGroup"); 222 if (nodeGroup == null) return null; 223 224 ActionResult result = nodeGroup.callByActionName("getWorkflowPath", ""); 225 return result.isSuccess() ? result.getResult() : null; 226 } 227 228 /** 229 * Outputs report to outputMultiplexer. 230 */ 231 private void reportToMultiplexer(String data) { 232 if (system() == null) return; 233 IIActorSystem iiSystem = (IIActorSystem) system(); 234 235 IIActorRef<?> multiplexer = iiSystem.getIIActor("outputMultiplexer"); 236 if (multiplexer == null) return; 237 238 JSONObject arg = new JSONObject(); 239 arg.put("source", "report-builder"); 240 arg.put("type", "plugin-result"); 241 arg.put("data", data); 242 multiplexer.callByActionName("add", arg.toString()); 243 } 244}