001/*
002 *  Copyright 2023 Anyware Services
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, software
011 *  distributed under the License is distributed on an "AS IS" BASIS,
012 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 *  See the License for the specific language governing permissions and
014 *  limitations under the License.
015 */
016package org.ametys.runtime.plugins.admin.statistics;
017
018import java.io.File;
019import java.io.IOException;
020import java.io.OutputStream;
021import java.nio.charset.StandardCharsets;
022import java.nio.file.Files;
023import java.nio.file.Path;
024import java.util.ArrayList;
025import java.util.HashMap;
026import java.util.List;
027import java.util.Map;
028
029import org.apache.avalon.framework.service.ServiceException;
030import org.apache.avalon.framework.service.ServiceManager;
031
032import org.ametys.core.ui.Callable;
033import org.ametys.core.util.JSONUtils;
034import org.ametys.runtime.i18n.I18nizableText;
035import org.ametys.runtime.plugin.component.AbstractThreadSafeComponentExtensionPoint;
036import org.ametys.runtime.util.AmetysHomeHelper;
037
038/**
039 * Extension point handling the remove statistics
040 */
041public class StatisticsProviderExtensionPoint extends AbstractThreadSafeComponentExtensionPoint<StatisticsProvider>
042{
043    /** The component role */
044    public static final String ROLE = StatisticsProviderExtensionPoint.class.getName();
045    
046    static final String _I18N_PREFIX = "{{i18n ";
047    static final String _I18N_SEPARATOR = ":";
048    static final String _I18N_SUFFIX = "}}";
049    
050    private static final String FILE = AmetysHomeHelper.AMETYS_HOME_ADMINISTRATOR_DIR + File.separator + "statistics.json";
051    private JSONUtils _jsonUtils;
052    
053    @Override
054    public void service(ServiceManager manager) throws ServiceException
055    {
056        super.service(manager);
057        _jsonUtils = (JSONUtils) manager.lookup(JSONUtils.ROLE);
058    }
059    
060    /**
061     * Generate the statistics
062     * @return The non null map of statistics
063     * @throws IOException If an error occurred while caching the results
064     */
065    public Map<String, Object> computeStatistics() throws IOException
066    {
067        Map<String, Object> jsonStatistics = new HashMap<>();
068        List<Map<String, Object>> treeStatistics = new ArrayList<>();
069        
070        for (String extensionId : getExtensionsIds())
071        {
072            StatisticsProvider extension = getExtension(extensionId);
073            
074            Statistics extensionStatistics = extension.getStatistics();
075            
076            jsonStatistics.putAll(extensionStatistics.toJSONForExport());
077            treeStatistics.add(extensionStatistics.toJSONTree());
078        }
079        
080        Path file = AmetysHomeHelper.getAmetysHome().toPath().resolve(FILE);
081        Files.createDirectories(file.getParent());
082
083        try (OutputStream os = Files.newOutputStream(file))
084        {
085            _jsonUtils.convertObjectToJson(os, treeStatistics);
086        }
087        
088        return jsonStatistics;
089    }
090    
091    /**
092     * Generate the statistics with tree information for client display
093     * @return The non null map of statistics
094     * @throws IOException If an error occurred reading the cache file
095     */
096    @Callable(context = "/admin", rights = "Runtime_Rights_Admin_Access")
097    public Object getStatisticsTree() throws IOException
098    {
099        Path file = AmetysHomeHelper.getAmetysHome().toPath().resolve(FILE);
100        if (Files.exists(file))
101        {
102            String json = Files.readString(file, StandardCharsets.UTF_8);
103            List<Object> tree = _jsonUtils.convertJsonToList(json);
104            _handleI18n(tree);
105            if (tree.size() > 0)
106            {
107                return tree;
108            }
109        }
110         
111        return Map.of(
112                "children", List.of(Map.of(
113                            "name", "error",
114                            "label", new I18nizableText("plugin.admin", "PLUGINS_ADMIN_STATISTICS_TOOL_EMPTY"),
115                            "value", new I18nizableText("plugin.admin", "PLUGINS_ADMIN_STATISTICS_TOOL_EMPTY2"),
116                            "iconCls", "ametysicon-sign-caution",
117                            "leaf", true)
118        ));
119    }
120
121    @SuppressWarnings("unchecked")
122    private void _handleI18n(Object object)
123    {
124        if (object instanceof List list)
125        {
126            for (Object node : list)
127            {
128                _handleI18n(node);
129            }
130        }
131        else if (object instanceof Map map)
132        {
133            Object label = map.get("label");
134            if (label instanceof String string
135                && string.startsWith(_I18N_PREFIX) && string.endsWith(_I18N_SUFFIX))
136            {
137                String[] i18n = string.substring(_I18N_PREFIX.length(), string.length() - _I18N_SUFFIX.length()).trim().split(_I18N_SEPARATOR);
138                I18nizableText i18nizableText = new I18nizableText(i18n[0], i18n[1]);
139                map.put("label", i18nizableText);
140            }
141            
142            Object children = map.get("children");
143            _handleI18n(children);
144        }
145    }
146}