001/* 002 * (C) Copyright 2006-2011 Nuxeo SA (http://nuxeo.com/) and others. 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 * 016 * Contributors: 017 * mcedica 018 */ 019package org.nuxeo.ecm.core.management.probes; 020 021import java.time.LocalDateTime; 022import java.time.ZoneId; 023import java.time.temporal.ChronoUnit; 024import java.util.Collection; 025import java.util.Collections; 026import java.util.Date; 027import java.util.HashMap; 028import java.util.HashSet; 029import java.util.Map; 030import java.util.Map.Entry; 031import java.util.Set; 032 033import org.apache.commons.logging.Log; 034import org.apache.commons.logging.LogFactory; 035import org.nuxeo.ecm.core.management.api.Probe; 036import org.nuxeo.ecm.core.management.api.ProbeInfo; 037import org.nuxeo.ecm.core.management.api.ProbeManager; 038import org.nuxeo.ecm.core.management.api.ProbeStatus; 039import org.nuxeo.ecm.core.management.statuses.HealthCheckResult; 040import org.nuxeo.runtime.api.Framework; 041import org.nuxeo.runtime.management.ManagementRuntimeException; 042 043public class ProbeManagerImpl implements ProbeManager { 044 045 protected static final Log log = LogFactory.getLog(ProbeManagerImpl.class); 046 047 protected final Map<Class<? extends Probe>, ProbeInfo> infosByTypes = new HashMap<Class<? extends Probe>, ProbeInfo>(); 048 049 protected final Map<String, ProbeInfo> infosByShortcuts = new HashMap<String, ProbeInfo>(); 050 051 protected final Map<String, Probe> probesByShortcuts = new HashMap<String, Probe>(); 052 053 protected final Map<String, ProbeInfo> probesForHealthCheck = new HashMap<String, ProbeInfo>(); 054 055 protected final Set<ProbeInfo> failed = new HashSet<ProbeInfo>(); 056 057 protected final Set<ProbeInfo> succeed = new HashSet<ProbeInfo>(); 058 059 public static final String DEFAULT_HEALTH_CHECK_INTERVAL_SECONDS_PROPERTY = "nuxeo.healthcheck.refresh.interval.seconds"; 060 061 public static final String DEFAULT_HEALTH_CHECK_INTERVAL_SECONDS = "20"; 062 063 protected Set<String> doExtractProbesName(Collection<ProbeInfo> runners) { 064 Set<String> names = new HashSet<String>(); 065 for (ProbeInfo runner : runners) { 066 names.add(runner.getShortcutName()); 067 } 068 return names; 069 } 070 071 @Override 072 public Collection<ProbeInfo> getAllProbeInfos() { 073 return Collections.unmodifiableCollection(infosByTypes.values()); 074 } 075 076 @Override 077 public Collection<ProbeInfo> getInSuccessProbeInfos() { 078 return Collections.unmodifiableCollection(succeed); 079 } 080 081 @Override 082 public Collection<ProbeInfo> getInFailureProbeInfos() { 083 return Collections.unmodifiableCollection(failed); 084 } 085 086 @Override 087 public Collection<String> getProbeNames() { 088 return infosByShortcuts.keySet(); 089 } 090 091 @Override 092 public int getProbesCount() { 093 return infosByTypes.size(); 094 } 095 096 @Override 097 public Collection<String> getProbesInError() { 098 return doExtractProbesName(failed); 099 } 100 101 @Override 102 public int getProbesInErrorCount() { 103 return failed.size(); 104 } 105 106 @Override 107 public Collection<String> getProbesInSuccess() { 108 return doExtractProbesName(succeed); 109 } 110 111 @Override 112 public int getProbesInSuccessCount() { 113 return succeed.size(); 114 } 115 116 @Override 117 public ProbeInfo getProbeInfo(Class<? extends Probe> probeClass) { 118 ProbeInfo info = infosByTypes.get(probeClass); 119 if (info == null) { 120 throw new IllegalArgumentException("no probe registered for " + probeClass); 121 } 122 return info; 123 } 124 125 @Override 126 public boolean runAllProbes() { 127 doRun(); 128 return getProbesInErrorCount() <= 0; 129 } 130 131 @Override 132 public ProbeInfo runProbe(ProbeInfo probe) { 133 doRunProbe(probe); 134 return probe; 135 } 136 137 @Override 138 public ProbeInfo runProbe(String name) { 139 ProbeInfo probeInfo = getProbeInfo(name); 140 if (probeInfo == null) { 141 log.warn("Probe " + name + " can not be found"); 142 return null; 143 } 144 return runProbe(probeInfo); 145 } 146 147 @Override 148 public ProbeInfo getProbeInfo(String name) { 149 return infosByShortcuts.get(name); 150 } 151 152 public void registerProbe(ProbeDescriptor descriptor) { 153 Class<? extends Probe> probeClass = descriptor.getProbeClass(); 154 Probe probe; 155 try { 156 probe = probeClass.newInstance(); 157 } catch (ReflectiveOperationException e) { 158 throw new ManagementRuntimeException("Cannot create management probe for " + descriptor); 159 } 160 161 ProbeInfoImpl info = new ProbeInfoImpl(descriptor); 162 infosByTypes.put(probeClass, info); 163 infosByShortcuts.put(descriptor.getShortcut(), info); 164 probesByShortcuts.put(descriptor.getShortcut(), probe); 165 } 166 167 public void unregisterProbe(ProbeDescriptor descriptor) { 168 Class<? extends Probe> probeClass = descriptor.getProbeClass(); 169 infosByTypes.remove(probeClass); 170 infosByShortcuts.remove(descriptor.getShortcut()); 171 probesForHealthCheck.remove(descriptor.getShortcut()); 172 } 173 174 protected void doRun() { 175 for (ProbeInfo probe : infosByTypes.values()) { 176 doRunProbe(probe); 177 } 178 } 179 180 protected static Long doGetDuration(Date fromDate, Date toDate) { 181 return toDate.getTime() - fromDate.getTime(); 182 } 183 184 protected void doRunProbe(ProbeInfo probe) { 185 if (!probe.isEnabled()) { 186 return; 187 } 188 boolean ok = false; 189 try { 190 ProbeInfoImpl probeInfoImpl = (ProbeInfoImpl) probe; 191 Thread currentThread = Thread.currentThread(); 192 ClassLoader lastLoader = currentThread.getContextClassLoader(); 193 currentThread.setContextClassLoader(ProbeInfoImpl.class.getClassLoader()); 194 probeInfoImpl.lastRunnedDate = new Date(); 195 probeInfoImpl.runnedCount += 1; 196 try { 197 Probe runnableProbe = probesByShortcuts.get(probe.getShortcutName()); 198 probeInfoImpl.lastStatus = runnableProbe.run(); 199 if (probeInfoImpl.lastStatus.isSuccess()) { 200 probeInfoImpl.lastSucceedDate = probeInfoImpl.lastRunnedDate; 201 probeInfoImpl.lastSuccessStatus = probeInfoImpl.lastStatus; 202 probeInfoImpl.successCount += 1; 203 } else { 204 probeInfoImpl.lastFailureStatus = probeInfoImpl.lastStatus; 205 probeInfoImpl.failureCount += 1; 206 probeInfoImpl.lastFailureDate = probeInfoImpl.lastRunnedDate; 207 } 208 } catch (RuntimeException e) { 209 probeInfoImpl.failureCount += 1; 210 probeInfoImpl.lastFailureDate = new Date(); 211 probeInfoImpl.lastFailureStatus = ProbeStatus.newError(e); 212 // then swallow exception 213 } finally { 214 probeInfoImpl.lastDuration = doGetDuration(probeInfoImpl.lastRunnedDate, new Date()); 215 currentThread.setContextClassLoader(lastLoader); 216 } 217 218 if (probe.isInError()) { 219 succeed.remove(probe); 220 failed.add(probe); 221 } else { 222 failed.remove(probe); 223 succeed.add(probe); 224 } 225 ok = true; 226 } finally { 227 if (!ok) { 228 succeed.remove(probe); 229 failed.add(probe); 230 } 231 } 232 } 233 234 @Override 235 public void registerProbeForHealthCheck(HealthCheckProbesDescriptor descriptor) { 236 String name = descriptor.getName(); 237 if (!descriptor.isEnabled()) { 238 if (probesForHealthCheck.containsKey(name)) { 239 probesForHealthCheck.remove(name); 240 return; 241 } 242 } 243 if (infosByShortcuts.containsKey(name)) { 244 probesForHealthCheck.put(name, getProbeInfo(name)); 245 } 246 } 247 248 @Override 249 public Collection<ProbeInfo> getHealthCheckProbes() { 250 return Collections.unmodifiableCollection(probesForHealthCheck.values()); 251 } 252 253 @Override 254 public HealthCheckResult getOrRunHealthChecks() { 255 for (Entry<String, ProbeInfo> es : probesForHealthCheck.entrySet()) { 256 String probeName = es.getKey(); 257 ProbeInfo probe = es.getValue(); 258 if (probe == null) { 259 log.warn("Probe:" + probeName + " does not exist, skipping it for the health check"); 260 continue; 261 } 262 getStatusOrRunProbe(probe, getDefaultCheckInterval()); 263 } 264 return new HealthCheckResult(probesForHealthCheck.values()); 265 } 266 267 @Override 268 public HealthCheckResult getOrRunHealthCheck(String name) throws IllegalArgumentException { 269 270 if (!probesForHealthCheck.containsKey(name)) { 271 throw new IllegalArgumentException("Probe:" + name + " does not exist, or not registed for the healthCheck"); 272 } 273 ProbeInfo probe = probesForHealthCheck.get(name); 274 getStatusOrRunProbe(probe, getDefaultCheckInterval()); 275 return new HealthCheckResult(Collections.singletonList(probe)); 276 } 277 278 protected void getStatusOrRunProbe(ProbeInfo probe, int refreshSeconds) { 279 LocalDateTime now = LocalDateTime.now(); 280 Date lastRunDate = probe.getLastRunnedDate(); 281 LocalDateTime lastRunDateTime = lastRunDate != null ? LocalDateTime.ofInstant(lastRunDate.toInstant(), 282 ZoneId.systemDefault()) : LocalDateTime.MIN; 283 if (ChronoUnit.SECONDS.between(lastRunDateTime, now) > refreshSeconds) { 284 doRunProbe(probe); 285 } 286 } 287 288 private int getDefaultCheckInterval() { 289 return Integer.parseInt(Framework.getProperty(DEFAULT_HEALTH_CHECK_INTERVAL_SECONDS_PROPERTY, 290 DEFAULT_HEALTH_CHECK_INTERVAL_SECONDS)); 291 292 } 293}