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", right = "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}