001/* 002 * Copyright 2015 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.jvmstatus; 017 018import java.io.File; 019import java.io.IOException; 020import java.lang.management.ManagementFactory; 021import java.lang.management.MemoryMXBean; 022import java.lang.management.RuntimeMXBean; 023import java.lang.management.ThreadMXBean; 024import java.util.ArrayList; 025import java.util.Collections; 026import java.util.Date; 027import java.util.HashMap; 028import java.util.HashSet; 029import java.util.List; 030import java.util.Map; 031import java.util.Set; 032import java.util.SortedMap; 033import java.util.TreeMap; 034 035import org.apache.avalon.framework.activity.Initializable; 036import org.apache.avalon.framework.component.Component; 037import org.apache.avalon.framework.logger.AbstractLogEnabled; 038import org.apache.avalon.framework.service.ServiceException; 039import org.apache.avalon.framework.service.ServiceManager; 040import org.apache.avalon.framework.service.Serviceable; 041import org.apache.commons.io.FileUtils; 042import org.rrd4j.core.Archive; 043import org.rrd4j.core.RrdDb; 044 045import org.ametys.core.ui.Callable; 046import org.ametys.core.util.DateUtils; 047import org.ametys.core.util.I18nUtils; 048import org.ametys.runtime.config.Config; 049import org.ametys.runtime.plugins.admin.jvmstatus.monitoring.MonitoringConstants; 050import org.ametys.runtime.plugins.admin.jvmstatus.monitoring.MonitoringExtensionPoint; 051import org.ametys.runtime.plugins.admin.jvmstatus.monitoring.SampleManager; 052import org.ametys.runtime.plugins.admin.jvmstatus.monitoring.alerts.AlertSampleManager; 053import org.ametys.runtime.plugins.admin.jvmstatus.monitoring.alerts.AlertSampleManager.Threshold; 054import org.ametys.runtime.servlet.RuntimeConfig; 055 056/** 057 * This helper allow to get information or runs some operations on JVM system 058 */ 059public class JVMStatusHelper extends AbstractLogEnabled implements Component, Serviceable, Initializable, MonitoringConstants 060{ 061 /** The monitoring extension point */ 062 private MonitoringExtensionPoint _monitoringExtensionPoint; 063 064 /** Component containing i18n utility methods */ 065 private I18nUtils _i18nUtils; 066 067 private String _rrdStoragePath; 068 069 @Override 070 public void service(ServiceManager manager) throws ServiceException 071 { 072 _monitoringExtensionPoint = (MonitoringExtensionPoint) manager.lookup(MonitoringExtensionPoint.ROLE); 073 _i18nUtils = (I18nUtils) manager.lookup(I18nUtils.ROLE); 074 } 075 076 public void initialize() throws Exception 077 { 078 _rrdStoragePath = FileUtils.getFile(RuntimeConfig.getInstance().getAmetysHome(), RRD_STORAGE_DIRECTORY).getPath(); 079 } 080 081 /** 082 * Runs a garbage collector. 083 * @return an empty map 084 */ 085 @Callable (rights = "Runtime_Rights_Admin_Access", context = "/admin") 086 public Map<String, Object> garbageCollect () 087 { 088 if (getLogger().isInfoEnabled()) 089 { 090 getLogger().info("Administrator is garbage collecting"); 091 } 092 093 System.gc(); 094 095 return Collections.EMPTY_MAP; 096 } 097 098 /** 099 * Retrieves information about the general status of the system 100 * @return a map containing the general status information 101 */ 102 @Callable (rights = "Runtime_Rights_Admin_Access", context = "/admin") 103 public Map<String, Object> getGeneralStatus() 104 { 105 Map<String, Object> result = new HashMap<>(); 106 107 result.put("osTime", DateUtils.dateToString(new Date())); 108 try 109 { 110 result.put("activeSessions", SessionCountListener.getSessionCount()); 111 } 112 catch (IllegalStateException e) 113 { 114 // empty : no value in activeSession means an error 115 } 116 117 try 118 { 119 result.put("activeSessionsDetail", ActiveSessionListener.getActiveSessionsAsJson()); 120 } 121 catch (IllegalStateException e) 122 { 123 // empty : no value in activeSession means an error 124 } 125 126 try 127 { 128 result.put("activeRequests", RequestCountListener.getCurrentRequestCount()); 129 } 130 catch (IllegalStateException e) 131 { 132 // empty : no value in activeSession means an error 133 } 134 135 136 ThreadMXBean tBean = ManagementFactory.getThreadMXBean(); 137 MemoryMXBean mBean = ManagementFactory.getMemoryMXBean(); 138 RuntimeMXBean rBean = ManagementFactory.getRuntimeMXBean(); 139 140 result.put("activeThreads", tBean.getThreadCount()); 141 long[] lockedThreads = ManagementFactory.getThreadMXBean().findMonitorDeadlockedThreads(); 142 143 result.put("deadlockThreads", lockedThreads != null ? String.valueOf(lockedThreads.length) : "0"); 144 145 result.put("heap-memory-max", mBean.getHeapMemoryUsage().getMax()); 146 result.put("heap-memory-used", mBean.getHeapMemoryUsage().getUsed()); 147 result.put("heap-memory-commited", mBean.getHeapMemoryUsage().getCommitted()); 148 149 150 result.put("startTime", DateUtils.dateToString(new Date(rBean.getStartTime()))); 151 152 return result; 153 } 154 155 /** 156 * Retrieves the monitoring data 157 * @return a map containing the monitoring data 158 */ 159 @Callable (rights = "Runtime_Rights_Admin_Access", context = "/admin") 160 public Map<String, Object> getMonitoringData() 161 { 162 Map<String, Object> result = new HashMap<> (); 163 164 Map<String, Object> samples = new HashMap<> (); 165 List<String> periods = new ArrayList<> (); 166 167 for (Period period : Period.values()) 168 { 169 periods.add(period.toString()); 170 } 171 172 samples.put("periods", periods); 173 174 List<Map<String, Object>> sampleList = new ArrayList<> (); 175 for (String extensionId : _monitoringExtensionPoint.getExtensionsIds()) 176 { 177 Map<String, Object> sample = new HashMap<> (); 178 SampleManager sampleManager = _monitoringExtensionPoint.getExtension(extensionId); 179 180 sample.put("id", sampleManager.getId()); 181 sample.put("label", _i18nUtils.translate(sampleManager.getLabel())); 182 sample.put("description", _i18nUtils.translate(sampleManager.getDescription())); 183 if (sampleManager instanceof AlertSampleManager && Config.getInstance().getValue("runtime.system.alerts.enable") == Boolean.TRUE) 184 { 185 Map<String, Object> thresholdValues = new HashMap<>(); 186 187 Map<String, Threshold> thresholds = ((AlertSampleManager) sampleManager).getThresholdValues(); 188 for (String datasourceName : thresholds.keySet()) 189 { 190 thresholdValues.put(datasourceName, thresholds.get(datasourceName).getValue()); 191 } 192 193 sample.put("thresholds", thresholdValues); 194 } 195 196 File rrdFile = new File(_rrdStoragePath, sampleManager.getId() + RRD_EXT); 197 if (getLogger().isDebugEnabled()) 198 { 199 getLogger().debug("Using RRD file: " + rrdFile); 200 } 201 202 try (RrdDb rrdDb = RrdDb.of(rrdFile.getPath())) 203 { 204 sample.put("ds", rrdDb.getDsNames()); 205 206 Set<String> consolidationFunction = new HashSet<>(); 207 for (int i = 0; i < rrdDb.getArcCount(); i++) 208 { 209 Archive archive = rrdDb.getArchive(i); 210 consolidationFunction.add(archive.getConsolFun().toString()); 211 } 212 sample.put("consolFun", consolidationFunction); 213 } 214 catch (Exception e) 215 { 216 getLogger().error("Unable to collect sample for: " + sampleManager.getId(), e); 217 } 218 219 sampleList.add(sample); 220 } 221 222 samples.put("sampleList", sampleList); 223 result.put("samples", samples); 224 return result; 225 } 226 227 /** 228 * Get the RRD sample data to JSON format 229 * @param sampleId the identifier of the sample to retrieve 230 * @return the sample data as a JSON 231 * @throws IOException if an error occurs 232 */ 233 @Callable (rights = "Runtime_Rights_Admin_Access", context = "/admin") 234 public List<Map<String, Object>> getSamplingData(String sampleId) throws IOException 235 { 236 if (!_monitoringExtensionPoint.hasExtension(sampleId)) 237 { 238 throw new IllegalArgumentException("No sample manager exists for: " + sampleId); 239 } 240 241 File rrdStorageDir = FileUtils.getFile(RuntimeConfig.getInstance().getAmetysHome(), RRD_STORAGE_DIRECTORY); 242 File rrdFile = new File(rrdStorageDir, sampleId + RRD_EXT); 243 if (getLogger().isDebugEnabled()) 244 { 245 getLogger().debug("Using RRD file: " + rrdFile); 246 } 247 248 SortedMap<Long, Map<String, Object>> data = new TreeMap<>(); 249 250 try (RrdDb rrdDb = RrdDb.of(rrdFile.getPath())) 251 { 252 for (Archive archive : _getRelevantArchives(rrdDb)) 253 { 254 long archiveStartTime = archive.getStartTime(); 255 for (int row = 0; row < archive.getRows(); row++) 256 { 257 long time = (archiveStartTime + row * archive.getArcStep()) * 1000; // time in ms 258 Map<String, Object> values; 259 if (data.containsKey(time)) 260 { 261 values = data.get(time); 262 } 263 else 264 { 265 values = new HashMap<>(); 266 data.put(time, values); 267 } 268 269 String[] dsNames = rrdDb.getDsNames(); 270 for (int dsIndex = 0; dsIndex < rrdDb.getDsCount(); dsIndex++) 271 { 272 String dsName = archive.getConsolFun().toString() + "_" + dsNames[dsIndex]; 273 double value = archive.getRobin(dsIndex).getValue(row); 274 if (!values.containsKey(dsName)) 275 { 276 if (Double.isNaN(value)) 277 { 278 value = 0; 279 } 280 values.put(dsName, value); 281 } 282 } 283 } 284 } 285 } 286 287 return _convertData(data); 288 } 289 290 private List<Archive> _getRelevantArchives(RrdDb rrdDb) 291 { 292 // Return all the archives, but in case of bad performance, it could 293 // be interesting to return jsut some of them 294 List<Archive> result = new ArrayList<>(); 295 for (int i = 0; i < rrdDb.getArcCount(); i++) 296 { 297 Archive archive = rrdDb.getArchive(i); 298 result.add(archive); 299 } 300 301 return result; 302 } 303 304 private List<Map<String, Object>> _convertData(SortedMap<Long, Map<String, Object>> data) 305 { 306 List<Map<String, Object>> result = new ArrayList<>(); 307 308 for (Long time : data.keySet()) 309 { 310 Map<String, Object> row = data.get(time); 311 row.put("time", time); 312 result.add(row); 313 } 314 315 return result; 316 } 317 318 /** 319 * Retrieves the monitoring data 320 * @return a map containing the monitoring data 321 */ 322 @Callable (rights = "Runtime_Rights_Admin_Access", context = "/admin") 323 public List<Map<String, String>> getSystemProperties() 324 { 325 RuntimeMXBean rBean = ManagementFactory.getRuntimeMXBean(); 326 327 Map<String, String> properties = rBean.getSystemProperties(); 328 return properties.entrySet().stream() 329 .filter(e -> e.getKey().indexOf(":") == -1) 330 .map(e -> Map.of("name", e.getKey(), "value", e.getValue())) 331 .toList(); 332 } 333}