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.lang.management.ManagementFactory;
019import java.lang.reflect.InvocationTargetException;
020import java.util.ArrayList;
021import java.util.Comparator;
022import java.util.List;
023import java.util.Map;
024import java.util.Map.Entry;
025import java.util.TreeMap;
026
027import org.apache.avalon.framework.service.ServiceException;
028import org.apache.avalon.framework.service.ServiceManager;
029import org.apache.avalon.framework.service.Serviceable;
030import org.apache.commons.lang3.StringUtils;
031import org.slf4j.Logger;
032import org.slf4j.LoggerFactory;
033
034import org.ametys.core.authentication.CredentialProvider;
035import org.ametys.core.authentication.CredentialProviderFactory;
036import org.ametys.core.authentication.CredentialProviderModel;
037import org.ametys.core.group.GroupDirectoryDAO;
038import org.ametys.core.group.directory.GroupDirectory;
039import org.ametys.core.group.directory.GroupDirectoryFactory;
040import org.ametys.core.user.directory.UserDirectory;
041import org.ametys.core.user.directory.UserDirectoryFactory;
042import org.ametys.core.user.population.UserPopulation;
043import org.ametys.core.user.population.UserPopulationDAO;
044import org.ametys.core.version.VersionsHandler;
045import org.ametys.runtime.config.Config;
046import org.ametys.runtime.i18n.I18nizableText;
047import org.ametys.runtime.plugin.PluginsManager;
048import org.ametys.runtime.plugin.component.PluginAware;
049import org.ametys.runtime.servlet.AnalyseFileForVirusHelper;
050
051import com.jsoftbiz.utils.OS;
052
053/**
054 * All statistics of kernel
055 */
056public class KernelStatisticsProvider implements StatisticsProvider, Serviceable, PluginAware
057{
058    private static String _tomcatVersion;
059    private static Logger _logger = LoggerFactory.getLogger(KernelStatisticsProvider.class.getName());
060    
061    private String _id;
062    private VersionsHandler _versionsHandler;
063    private UserPopulationDAO _userPopulationDAO;
064    private UserDirectoryFactory _userDirectoryFactory;
065    private CredentialProviderFactory _credentialProviderFactory;
066    private GroupDirectoryDAO _groupDirectoryDAO;
067    private GroupDirectoryFactory _groupDirectoryFactory;
068
069    public void service(ServiceManager manager) throws ServiceException
070    {
071        _versionsHandler = (VersionsHandler) manager.lookup(VersionsHandler.ROLE);
072        _userPopulationDAO = (UserPopulationDAO) manager.lookup(UserPopulationDAO.ROLE);
073        _userDirectoryFactory = (UserDirectoryFactory) manager.lookup(UserDirectoryFactory.ROLE);
074        _credentialProviderFactory = (CredentialProviderFactory) manager.lookup(CredentialProviderFactory.ROLE);
075        _groupDirectoryDAO = (GroupDirectoryDAO) manager.lookup(GroupDirectoryDAO.ROLE);
076        _groupDirectoryFactory = (GroupDirectoryFactory) manager.lookup(GroupDirectoryFactory.ROLE);
077    }
078    
079    public void setPluginInfo(String pluginName, String featureName, String id)
080    {
081        _id = id;
082    }
083    
084    /**
085     * Get the Apache Tomcat version such as "9.0.46.0"
086     * @return The version of Apache Tomcat or null if an error occurred while getting it
087     */
088    @SuppressWarnings("unchecked")
089    public static synchronized String getTomcatVersion()
090    {
091        if (_tomcatVersion == null)
092        {
093            try
094            {
095                Class tomcatServerInfo = KernelStatisticsProvider.class.getClassLoader().loadClass("org.apache.catalina.util.ServerInfo");
096                _tomcatVersion = (String) tomcatServerInfo.getMethod("getServerNumber").invoke(tomcatServerInfo);
097            }
098            catch (ClassNotFoundException | NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e)
099            {
100                // Not computable...
101                _logger.warn("Cannot get the tomcat version", e);
102            }
103        }
104        return _tomcatVersion;
105    }
106    
107    public Statistics getStatistics()
108    {
109        return new StatisticsNode(
110                _id,
111                new I18nizableText("plugin.admin", "PLUGINS_ADMIN_STATISTICS_KERNEL_LABEL"),
112                "ametysicon-trademark-ametys",
113                null,
114                List.of(
115                    _getVersionsStatistics(),
116                    _getPluginsStatistics(),
117                    _getConfigurationStatistics(),
118                    _getPopulationsAndGropDirectoriesStatistics(),
119                    _getServerStatistics()
120                ),
121                true
122            );
123    }
124    
125    
126    private Statistics _getServerStatistics()
127    {
128        return new StatisticsNode(
129                "server",
130                new I18nizableText("plugin.admin", "PLUGINS_ADMIN_STATISTICS_KERNEL_SERVER_LABEL"),
131                "ametysicon-system-server-sync",
132                null,
133                List.of(
134                    new StatisticsValue(
135                        "osname",
136                        new I18nizableText("plugin.admin", "PLUGINS_ADMIN_STATISTICS_KERNEL_SERVER_OS_LABEL"),
137                        "ametysicon-system-terminal",
138                        OS.OS.getPlatformName() + " (" + OS.OS.getVersion() + ")"
139                    ),
140                    new StatisticsValue(
141                        "cpu",
142                        new I18nizableText("plugin.admin", "PLUGINS_ADMIN_STATISTICS_KERNEL_SERVER_CPU_LABEL"),
143                        "ametysicon-trademark-jackrabbit",
144                        String.valueOf(ManagementFactory.getOperatingSystemMXBean().getAvailableProcessors()) + " x " + OS.OS.getArch()
145                    ),
146                    new StatisticsValue(
147                        "ram",
148                        new I18nizableText("plugin.admin", "PLUGINS_ADMIN_STATISTICS_KERNEL_SERVER_RAM_LABEL"),
149                        "ametysicon-system-sgbd",
150                        ManagementFactory.getMemoryMXBean().getHeapMemoryUsage().getMax()
151                    ),
152                    new StatisticsValue(
153                        "vmvendor",
154                        new I18nizableText("plugin.admin", "PLUGINS_ADMIN_STATISTICS_KERNEL_SERVER_VMVENDOR_LABEL"),
155                        "ametysicon-trademark-jstack",
156                        System.getProperty("java.vendor") + " " + ManagementFactory.getRuntimeMXBean().getVmName()
157                    ),
158                    new StatisticsValue(
159                        "vmversion",
160                        new I18nizableText("plugin.admin", "PLUGINS_ADMIN_STATISTICS_KERNEL_SERVER_VMVERSION_LABEL"),
161                        "ametysicon-trademark-jstack",
162                        System.getProperty("java.version")
163                    ),
164                    new StatisticsValue(
165                        "tomcatversion",
166                        new I18nizableText("plugin.admin", "PLUGINS_ADMIN_STATISTICS_KERNEL_SERVER_TOMCATVERSION_LABEL"),
167                        "ametysicon-trademark-tomcat",
168                        getTomcatVersion()
169                    )
170                ),
171                false
172                );
173    }
174    
175    private Statistics _getVersionsStatistics()
176    {
177        return new StatisticsNode(
178            "versions",
179            new I18nizableText("plugin.admin", "PLUGINS_ADMIN_STATISTICS_KERNEL_VERSIONS_LABEL"),
180            "ametysicon-maths-number-zero-one",
181            null,
182            _getVersionsChildren(),
183            true
184        );
185    }
186    
187    private List<Statistics> _getVersionsChildren()
188    {
189        return _versionsHandler.getVersions().stream()
190            .map(v -> new StatisticsValue(
191                v.getName(),
192                new I18nizableText(v.getName()),
193                "ametysicon-trademark-ametys",
194                v.getVersion())
195            )
196            .map(Statistics.class::cast)
197            .toList();
198    }
199    
200    private Statistics _getPluginsStatistics()
201    {
202        List<Statistics> plugins = PluginsManager.getInstance().getBundledPluginsNames()
203                                                                    .stream()
204                                                                    .sorted(Comparator.naturalOrder())
205                                                                    .map(pluginName -> new StatisticsValue(pluginName,
206                                                                                                           new I18nizableText(pluginName),
207                                                                                                           "ametysicon-puzzle-piece1", 
208                                                                                                           PluginsManager.getInstance().getVersion(pluginName)))
209                                                                    .map(Statistics.class::cast)
210                                                                    .toList();
211        
212        return new StatisticsNode(
213            "plugins",
214            new I18nizableText("plugin.admin", "PLUGINS_ADMIN_STATISTICS_KERNEL_PLUGINS_LABEL"),
215            "ametysicon-puzzle-piece1",
216            null,
217            plugins,
218            false
219        );
220    }
221    
222    private Statistics _getPopulationsAndGropDirectoriesStatistics()
223    {
224        long populations = 0;
225        
226        Map<String, Long> userDirectoriesCount = new TreeMap<>();
227        Map<String, Long> credentialProvidersCount = new TreeMap<>();
228        
229        for (UserPopulation userPopulation : _userPopulationDAO.getUserPopulations(false))
230        {
231            populations++;
232            
233            for (UserDirectory userDirectory : userPopulation.getUserDirectories())
234            {
235                Long value = userDirectoriesCount.getOrDefault(userDirectory.getUserDirectoryModelId(), 0L);
236                userDirectoriesCount.put(userDirectory.getUserDirectoryModelId(), value + 1);
237            }
238            
239            for (CredentialProvider credentialProvider : userPopulation.getCredentialProviders())
240            {
241                Long value = credentialProvidersCount.getOrDefault(credentialProvider.getCredentialProviderModelId(), 0L);
242                credentialProvidersCount.put(credentialProvider.getCredentialProviderModelId(), value + 1);
243            }
244        }
245        
246        List<Statistics> userDirectories = new ArrayList<>();
247        for (Entry<String, Long> entry : userDirectoriesCount.entrySet())
248        {
249            userDirectories.add(new StatisticsValue(
250                entry.getKey(),
251                _userDirectoryFactory.getExtension(entry.getKey()).getLabel(),
252                "ametysicon-body-idcard-white",
253                entry.getValue()
254            ));
255        }
256        
257        List<Statistics> credentialProviders = new ArrayList<>();
258        for (Entry<String, Long> entry : credentialProvidersCount.entrySet())
259        {
260            CredentialProviderModel cp = _credentialProviderFactory.getExtension(entry.getKey());
261            credentialProviders.add(new StatisticsValue(
262                entry.getKey(),
263                cp.getLabel(),
264                StringUtils.defaultIfBlank(cp.getIconGlyph(), "ametysicon-body-idcard-badge"),
265                entry.getValue()
266            ));
267        }
268        
269        long groups = 0;
270
271        Map<String, Long> groupDirectoriesCount = new TreeMap<>();
272        for (GroupDirectory groupDirectory : _groupDirectoryDAO.getGroupDirectories())
273        {
274            groups++;
275            
276            Long value = groupDirectoriesCount.getOrDefault(groupDirectory.getGroupDirectoryModelId(), 0L);
277            groupDirectoriesCount.put(groupDirectory.getGroupDirectoryModelId(), value + 1);
278        }
279        
280        List<Statistics> groupDirectories = new ArrayList<>();
281        for (Entry<String, Long> entry : groupDirectoriesCount.entrySet())
282        {
283            groupDirectories.add(new StatisticsValue(
284                entry.getKey(),
285                _groupDirectoryFactory.getExtension(entry.getKey()).getLabel(),
286                "ametysicon-body-people",
287                entry.getValue()
288            ));
289        }
290        
291        return new StatisticsNode(
292            "populations-groupdirectories",
293            new I18nizableText("plugin.admin", "PLUGINS_ADMIN_STATISTICS_KERNEL_USERPOPULATIONSANDGROUPDIRECTORIES_LABEL"),
294            "ametysicon-body-group",
295            populations + groups,
296            List.of(
297                new StatisticsNode(
298                    "populations",
299                    new I18nizableText("plugin.admin", "PLUGINS_ADMIN_STATISTICS_KERNEL_USERPOPULATIONSANDGROUPDIRECTORIES_POPULATIONS_LABEL"),
300                    "ametysicon-body-people",
301                    populations,
302                    List.of(
303                        new StatisticsNode(
304                            "userdirectories",
305                            new I18nizableText("plugin.admin", "PLUGINS_ADMIN_STATISTICS_KERNEL_USERPOPULATIONSANDGROUPDIRECTORIES_POPULATIONS_USERDIRECTORIES_LABEL"),
306                            "ametysicon-body-idcard-white",
307                            null,
308                            userDirectories,
309                            false
310                        ),
311                        new StatisticsNode(
312                            "credentialproviders",
313                            new I18nizableText("plugin.admin", "PLUGINS_ADMIN_STATISTICS_KERNEL_USERPOPULATIONSANDGROUPDIRECTORIES_POPULATIONS_CREDENTIALPROVIDERS_LABEL"),
314                            "ametysicon-body-idcard-badge",
315                            null,
316                            credentialProviders,
317                            false
318                        )
319                    ),
320                    false
321                ),
322                new StatisticsNode(
323                    "groupdirectories",
324                    new I18nizableText("plugin.admin", "PLUGINS_ADMIN_STATISTICS_KERNEL_USERPOPULATIONSANDGROUPDIRECTORIES_GROUPDIRECTORIES_LABEL"),
325                    "ametysicon-body-people",
326                    groups,
327                    groupDirectories,
328                    false
329                )
330            ),
331            false
332        );
333    }
334    
335    private StatisticsNode _getConfigurationStatistics()
336    {
337        boolean configProd = !Config.getInstance().getValue("runtime.mode.dev", false, true);
338        String configCaptchaType = Config.getInstance().getValue("runtime.captcha.type", false, "");
339        Long configMaxUpload = Config.getInstance().getValue("runtime.upload.max-size", false, 0L);
340        boolean configGravatar = Config.getInstance().getValue("runtime.userprofile.imagesource.gravatar", false, true);
341        
342        return new StatisticsNode(
343            "config",
344            new I18nizableText("plugin.admin", "PLUGINS_ADMIN_STATISTICS_KERNEL_CONFIG_LABEL"),
345            "ametysicon-gear39",
346            null,
347            List.of(
348                new StatisticsValue(
349                    "production",
350                    new I18nizableText("plugin.admin", "PLUGINS_ADMIN_STATISTICS_KERNEL_CONFIG_PRODUCTION_LABEL"),
351                    "ametysicon-movie16",
352                    configProd
353                ),
354                new StatisticsValue(
355                    "captcha",
356                    new I18nizableText("plugin.admin", "PLUGINS_ADMIN_STATISTICS_KERNEL_CONFIG_CAPTCHA_LABEL"),
357                    "ametysicon-abecedary4",
358                    configCaptchaType
359                ),
360                new StatisticsValue(
361                    "upload",
362                    new I18nizableText("plugin.admin", "PLUGINS_ADMIN_STATISTICS_KERNEL_CONFIG_UPLOAD_LABEL"),
363                    "ametysicon-arrow-up-from",
364                    configMaxUpload
365                ),
366                new StatisticsValue(
367                    "gravatar",
368                    new I18nizableText("plugin.admin", "PLUGINS_ADMIN_STATISTICS_KERNEL_CONFIG_GRAVATAR_LABEL"),
369                    "ametysicon-body-idcard-black",
370                    configGravatar
371                ),
372                new StatisticsValue(
373                    "antivirus", 
374                    new I18nizableText("plugin.admin", "PLUGINS_ADMIN_STATISTICS_KERNEL_SERVER_ANTIVIRUS_LABEL"), 
375                    "ametysicon-arrow-down-in", 
376                    AnalyseFileForVirusHelper.isAntivirusEnabled()
377                )
378            ),
379            false
380        );
381    }
382}