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.sections.basic; 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 java.util.logging.Logger; 027 028/** 029 * IIAR wrapper for {@link JsonStateSection}. 030 * 031 * <p>Exposes the POJO's methods as actions via {@code @Action} annotations. 032 * Retrieves JsonState from a target actor and formats it as YAML.</p> 033 * 034 * <h2>Usage in workflow YAML:</h2> 035 * 036 * <p>The actor name encodes the target actor using ":" separator:</p> 037 * <pre>{@code 038 * - actor: loader 039 * method: createChild 040 * arguments: ["reportBuilder", "state:nodeGroup", "...JsonStateSectionIIAR"] 041 * }</pre> 042 * 043 * <p>This creates a section that outputs nodeGroup's JsonState.</p> 044 * 045 * <p>Optional JSON path can be specified after another ":":</p> 046 * <pre>{@code 047 * - actor: loader 048 * method: createChild 049 * arguments: ["reportBuilder", "state:nodeGroup:cluster.nodes", "...JsonStateSectionIIAR"] 050 * }</pre> 051 * 052 * <p>This outputs only the "cluster.nodes" path from nodeGroup's JsonState.</p> 053 * 054 * @author devteam@scivicslab.com 055 * @since 2.16.0 056 */ 057public class JsonStateSectionIIAR extends IIActorRef<JsonStateSection> { 058 059 private static final Logger logger = Logger.getLogger(JsonStateSectionIIAR.class.getName()); 060 061 private String targetActorName; 062 private String jsonPath; 063 064 /** 065 * Constructs the IIAR with a new POJO instance. 066 * 067 * <p>The actor name is parsed to extract target actor and optional path:</p> 068 * <ul> 069 * <li>"state:nodeGroup" → target=nodeGroup, path=null</li> 070 * <li>"state:nodeGroup:cluster.nodes" → target=nodeGroup, path=cluster.nodes</li> 071 * <li>"myState" → target=myState, path=null (no ":" prefix)</li> 072 * </ul> 073 * 074 * @param actorName the actor name (may encode target actor) 075 * @param system the actor system 076 */ 077 public JsonStateSectionIIAR(String actorName, IIActorSystem system) { 078 super(actorName, new JsonStateSection(), system); 079 parseActorName(actorName); 080 } 081 082 /** 083 * Parses the actor name to extract target actor and optional path. 084 */ 085 private void parseActorName(String actorName) { 086 if (actorName == null) { 087 return; 088 } 089 090 // Format: "prefix:targetActor" or "prefix:targetActor:jsonPath" 091 String[] parts = actorName.split(":", 3); 092 if (parts.length >= 2) { 093 // Has ":" separator - second part is target actor 094 this.targetActorName = parts[1]; 095 if (parts.length >= 3) { 096 this.jsonPath = parts[2]; 097 } 098 } else { 099 // No ":" - use the whole name as target 100 this.targetActorName = actorName; 101 } 102 103 object.setActorName(this.targetActorName); 104 object.setJsonPath(this.jsonPath); 105 } 106 107 /** 108 * Refreshes the YAML content from the target actor's JsonState. 109 * 110 * <p>Called automatically before generate() to ensure fresh data.</p> 111 */ 112 private void refreshContent() { 113 if (targetActorName == null || actorSystem == null) { 114 return; 115 } 116 117 if (!(actorSystem instanceof IIActorSystem)) { 118 return; 119 } 120 IIActorSystem iiSystem = (IIActorSystem) actorSystem; 121 122 IIActorRef<?> targetActor = iiSystem.getIIActor(targetActorName); 123 if (targetActor == null) { 124 logger.warning("JsonStateSectionIIAR: target actor not found: " + targetActorName); 125 return; 126 } 127 128 JsonState jsonState = targetActor.json(); 129 if (jsonState == null) { 130 logger.warning("JsonStateSectionIIAR: target actor has no JsonState: " + targetActorName); 131 return; 132 } 133 134 String yamlContent = jsonState.toStringOfYaml(jsonPath); 135 object.setYamlContent(yamlContent); 136 } 137 138 // ======================================================================== 139 // Actions - expose POJO methods 140 // ======================================================================== 141 142 @Action("generate") 143 public ActionResult generate(String args) { 144 // Refresh content before generating 145 refreshContent(); 146 String content = object.generate(); 147 return new ActionResult(true, content); 148 } 149 150 @Action("getTitle") 151 public ActionResult getTitle(String args) { 152 String title = object.getTitle(); 153 return new ActionResult(true, title != null ? title : ""); 154 } 155 156 /** 157 * Sets the target actor name dynamically. 158 * 159 * @param args the target actor name 160 * @return action result 161 */ 162 @Action("setTargetActor") 163 public ActionResult setTargetActor(String args) { 164 if (args != null && !args.isEmpty()) { 165 this.targetActorName = args.trim(); 166 object.setActorName(this.targetActorName); 167 } 168 return new ActionResult(true, "Target actor set: " + targetActorName); 169 } 170 171 /** 172 * Sets the JSON path filter dynamically. 173 * 174 * @param args the JSON path 175 * @return action result 176 */ 177 @Action("setJsonPath") 178 public ActionResult setJsonPath(String args) { 179 this.jsonPath = (args != null && !args.isEmpty()) ? args.trim() : null; 180 object.setJsonPath(this.jsonPath); 181 return new ActionResult(true, "JSON path set: " + jsonPath); 182 } 183}