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.accumulator; 019 020import java.util.concurrent.ExecutionException; 021import java.util.logging.Level; 022import java.util.logging.Logger; 023 024import org.json.JSONObject; 025 026import com.scivicslab.pojoactor.core.Action; 027import com.scivicslab.pojoactor.core.ActionResult; 028import com.scivicslab.pojoactor.workflow.IIActorRef; 029import com.scivicslab.pojoactor.workflow.IIActorSystem; 030 031/** 032 * Actor reference for MultiplexerAccumulator. 033 * 034 * <p>This class wraps a {@link MultiplexerAccumulator} as an actor, allowing 035 * other actors to send messages to it via {@code callByActionName}. The actor 036 * forwards all output to its registered target accumulators (Console, File, Database).</p> 037 * 038 * <p>This class replaces the former {@code LoggingAccumulatorIIAR} with a more 039 * flexible architecture that supports multiple output destinations.</p> 040 * 041 * <h2>Supported Actions</h2> 042 * <ul> 043 * <li>{@code add} - Adds output to all registered accumulators</li> 044 * <li>{@code getSummary} - Returns formatted summary</li> 045 * <li>{@code getCount} - Returns the number of added entries</li> 046 * <li>{@code clear} - Clears all accumulated entries</li> 047 * </ul> 048 * 049 * <h2>Message Format for "add" Action</h2> 050 * <pre>{@code 051 * { 052 * "source": "node-localhost", 053 * "type": "stdout", 054 * "data": "command output here" 055 * } 056 * }</pre> 057 * 058 * <h2>Usage</h2> 059 * <pre>{@code 060 * // Create and register the actor 061 * MultiplexerAccumulator multiplexer = new MultiplexerAccumulator(); 062 * multiplexer.addTarget(new ConsoleAccumulator()); 063 * multiplexer.addTarget(new FileAccumulator(logFilePath)); 064 * multiplexer.addTarget(new DatabaseAccumulator(logStoreActor, dbExecutor, sessionId)); 065 * 066 * MultiplexerAccumulatorIIAR actor = new MultiplexerAccumulatorIIAR( 067 * "outputMultiplexer", multiplexer, system); 068 * system.addIIActor(actor); 069 * 070 * // Other actors can send messages by name 071 * IIActorRef<?> multiplexerActor = system.getIIActor("outputMultiplexer"); 072 * multiplexerActor.callByActionName("add", jsonArg); 073 * }</pre> 074 * 075 * @author devteam@scivicslab.com 076 * @since 2.12.0 077 */ 078public class MultiplexerAccumulatorIIAR extends IIActorRef<MultiplexerAccumulator> { 079 080 private final Logger logger; 081 082 /** 083 * Constructs a new MultiplexerAccumulatorIIAR. 084 * 085 * @param actorName the name of this actor (e.g., "outputMultiplexer") 086 * @param object the MultiplexerAccumulator instance 087 * @param system the actor system 088 */ 089 public MultiplexerAccumulatorIIAR(String actorName, MultiplexerAccumulator object, IIActorSystem system) { 090 super(actorName, object, system); 091 this.logger = Logger.getLogger(actorName); 092 } 093 094 // ======================================================================== 095 // Actions 096 // ======================================================================== 097 098 /** 099 * Adds output to all registered accumulators. 100 * 101 * <p>Parses the JSON argument and forwards to the multiplexer, which then 102 * distributes to all registered target accumulators.</p> 103 * 104 * @param arg JSON object with source, type, and data fields 105 * @return ActionResult indicating success or failure 106 */ 107 @Action("add") 108 public ActionResult add(String arg) { 109 // Note: Do NOT log here - it causes infinite loop via MultiplexerLogHandler 110 try { 111 JSONObject json = new JSONObject(arg); 112 String source = json.getString("source"); 113 String type = json.getString("type"); 114 String data = json.getString("data"); 115 116 // Forward to multiplexer (which distributes to all targets) 117 this.tell(acc -> acc.add(source, type, data)).get(); 118 119 return new ActionResult(true, "Added"); 120 } catch (Exception e) { 121 logger.log(Level.SEVERE, "Error in add", e); 122 return new ActionResult(false, "Error: " + e.getMessage()); 123 } 124 } 125 126 /** 127 * Returns formatted summary from all accumulators. 128 * 129 * @param arg not used 130 * @return ActionResult with the formatted summary 131 */ 132 @Action("getSummary") 133 public ActionResult getSummary(String arg) { 134 try { 135 String summary = this.ask(MultiplexerAccumulator::getSummary).get(); 136 return new ActionResult(true, summary); 137 } catch (ExecutionException | InterruptedException e) { 138 logger.log(Level.SEVERE, "Error in getSummary", e); 139 return new ActionResult(false, "Error: " + e.getMessage()); 140 } 141 } 142 143 /** 144 * Returns the number of added entries. 145 * 146 * @param arg not used 147 * @return ActionResult with the count 148 */ 149 @Action("getCount") 150 public ActionResult getCount(String arg) { 151 try { 152 int count = this.ask(MultiplexerAccumulator::getCount).get(); 153 return new ActionResult(true, String.valueOf(count)); 154 } catch (ExecutionException | InterruptedException e) { 155 logger.log(Level.SEVERE, "Error in getCount", e); 156 return new ActionResult(false, "Error: " + e.getMessage()); 157 } 158 } 159 160 /** 161 * Clears all accumulated entries. 162 * 163 * @param arg not used 164 * @return ActionResult indicating success 165 */ 166 @Action("clear") 167 public ActionResult clear(String arg) { 168 try { 169 this.tell(MultiplexerAccumulator::clear).get(); 170 return new ActionResult(true, "Cleared"); 171 } catch (ExecutionException | InterruptedException e) { 172 logger.log(Level.SEVERE, "Error in clear", e); 173 return new ActionResult(false, "Error: " + e.getMessage()); 174 } 175 } 176}