001/*
002 * Copyright 2025 devteam@scivics-lab.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;
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.actoriac.log.DistributedLogStore;
027import com.scivicslab.actoriac.log.LogLevel;
028import com.scivicslab.pojoactor.core.ActionResult;
029import com.scivicslab.pojoactor.core.accumulator.Accumulator;
030import com.scivicslab.pojoactor.workflow.IIActorRef;
031import com.scivicslab.pojoactor.workflow.IIActorSystem;
032
033/**
034 * Accumulator actor reference that also logs to H2 database.
035 *
036 * <p>This class extends the standard accumulator functionality to also write
037 * logs to the H2LogStore for persistent storage. Each node's output is stored
038 * with the node ID for later querying.</p>
039 *
040 * <h2>Supported Actions</h2>
041 * <ul>
042 *   <li>{@code add} - Adds a result and logs to database</li>
043 *   <li>{@code getSummary} - Returns formatted summary of all results</li>
044 *   <li>{@code getCount} - Returns the number of added results</li>
045 *   <li>{@code clear} - Clears all accumulated results</li>
046 * </ul>
047 *
048 * @author devteam@scivics-lab.com
049 * @since 2.9.0
050 */
051public class LoggingAccumulatorIIAR extends IIActorRef<Accumulator> {
052
053    private final Logger logger;
054    private final DistributedLogStore logStore;
055    private final long sessionId;
056
057    /**
058     * Constructs a new LoggingAccumulatorIIAR.
059     *
060     * @param actorName the name of this actor
061     * @param object the Accumulator implementation
062     * @param system the actor system
063     * @param logStore the distributed log store for database logging
064     * @param sessionId the session ID for this workflow execution
065     */
066    public LoggingAccumulatorIIAR(String actorName, Accumulator object, IIActorSystem system,
067                                   DistributedLogStore logStore, long sessionId) {
068        super(actorName, object, system);
069        this.logger = Logger.getLogger(actorName);
070        this.logStore = logStore;
071        this.sessionId = sessionId;
072    }
073
074    @Override
075    public ActionResult callByActionName(String actionName, String arg) {
076        logger.fine(String.format("actionName = %s, arg = %s", actionName, arg));
077
078        try {
079            switch (actionName) {
080                case "add":
081                    return handleAdd(arg);
082
083                case "getSummary":
084                    return handleGetSummary();
085
086                case "getCount":
087                    return handleGetCount();
088
089                case "clear":
090                    return handleClear();
091
092                default:
093                    logger.warning("Unknown action: " + actionName);
094                    return new ActionResult(false, "Unknown action: " + actionName);
095            }
096        } catch (Exception e) {
097            logger.log(Level.SEVERE, "Error in " + actionName, e);
098            return new ActionResult(false, "Error: " + e.getMessage());
099        }
100    }
101
102    /**
103     * Handles the add action.
104     *
105     * <p>Adds the result to the accumulator (for console output) and also
106     * logs to the H2 database for persistent storage.</p>
107     *
108     * @param arg JSON object with source, type, and data fields
109     * @return ActionResult indicating success or failure
110     */
111    private ActionResult handleAdd(String arg) throws ExecutionException, InterruptedException {
112        JSONObject json = new JSONObject(arg);
113        String source = json.getString("source");
114        String type = json.getString("type");
115        String data = json.getString("data");
116
117        // Add to accumulator (console output)
118        this.tell(acc -> acc.add(source, type, data)).get();
119
120        // Log to H2 database
121        if (logStore != null && sessionId >= 0) {
122            // source = node ID (e.g., "node-192.168.1.1")
123            // type = vertex YAML snippet (what step is being executed)
124            // data = command output
125            logStore.logAction(sessionId, source, type, "executeCommand", 0, 0L, data);
126        }
127
128        return new ActionResult(true, "Added");
129    }
130
131    /**
132     * Handles the getSummary action.
133     *
134     * @return ActionResult with the formatted summary
135     */
136    private ActionResult handleGetSummary() throws ExecutionException, InterruptedException {
137        String summary = this.ask(Accumulator::getSummary).get();
138        return new ActionResult(true, summary);
139    }
140
141    /**
142     * Handles the getCount action.
143     *
144     * @return ActionResult with the count
145     */
146    private ActionResult handleGetCount() throws ExecutionException, InterruptedException {
147        int count = this.ask(Accumulator::getCount).get();
148        return new ActionResult(true, String.valueOf(count));
149    }
150
151    /**
152     * Handles the clear action.
153     *
154     * @return ActionResult indicating success
155     */
156    private ActionResult handleClear() throws ExecutionException, InterruptedException {
157        this.tell(Accumulator::clear).get();
158        return new ActionResult(true, "Cleared");
159    }
160}