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.successCount += 1; 201 probeInfoImpl.lastSucceedDate = probeInfoImpl.lastRunnedDate; 202 probeInfoImpl.lastSuccessStatus = probeInfoImpl.lastStatus; 203 } else { 204 probeInfoImpl.failureCount += 1; 205 probeInfoImpl.lastFailureDate = probeInfoImpl.lastRunnedDate; 206 probeInfoImpl.lastFailureStatus = probeInfoImpl.lastStatus; 207 } 208 } catch (RuntimeException e) { 209 probeInfoImpl.failureCount += 1; 210 probeInfoImpl.lastFailureDate = new Date(); 211 probeInfoImpl.lastFailureStatus = ProbeStatus.newError(e); 212 // then swallow exception but wrap error to update status 213 probeInfoImpl.lastStatus = ProbeStatus.newError(e); 214 } finally { 215 probeInfoImpl.lastDuration = doGetDuration(probeInfoImpl.lastRunnedDate, new Date()); 216 currentThread.setContextClassLoader(lastLoader); 217 } 218 219 if (probe.isInError()) { 220 succeed.remove(probe); 221 failed.add(probe); 222 } else { 223 failed.remove(probe); 224 succeed.add(probe); 225 } 226 ok = true; 227 } finally { 228 if (!ok) { 229 succeed.remove(probe); 230 failed.add(probe); 231 } 232 } 233 } 234 235 @Override 236 public void registerProbeForHealthCheck(HealthCheckProbesDescriptor descriptor) { 237 String name = descriptor.getName(); 238 if (!descriptor.isEnabled()) { 239 if (probesForHealthCheck.containsKey(name)) { 240 probesForHealthCheck.remove(name); 241 return; 242 } 243 } 244 if (infosByShortcuts.containsKey(name)) { 245 probesForHealthCheck.put(name, getProbeInfo(name)); 246 } 247 } 248 249 @Override 250 public Collection<ProbeInfo> getHealthCheckProbes() { 251 return Collections.unmodifiableCollection(probesForHealthCheck.values()); 252 } 253 254 @Override 255 public HealthCheckResult getOrRunHealthChecks() { 256 for (Entry<String, ProbeInfo> es : probesForHealthCheck.entrySet()) { 257 String probeName = es.getKey(); 258 ProbeInfo probe = es.getValue(); 259 if (probe == null) { 260 log.warn("Probe:" + probeName + " does not exist, skipping it for the health check"); 261 continue; 262 } 263 getStatusOrRunProbe(probe, getDefaultCheckInterval()); 264 } 265 return new HealthCheckResult(probesForHealthCheck.values()); 266 } 267 268 @Override 269 public HealthCheckResult getOrRunHealthCheck(String name) throws IllegalArgumentException { 270 271 if (!probesForHealthCheck.containsKey(name)) { 272 throw new IllegalArgumentException("Probe:" + name + " does not exist, or not registed for the healthCheck"); 273 } 274 ProbeInfo probe = probesForHealthCheck.get(name); 275 getStatusOrRunProbe(probe, getDefaultCheckInterval()); 276 return new HealthCheckResult(Collections.singletonList(probe)); 277 } 278 279 protected void getStatusOrRunProbe(ProbeInfo probe, int refreshSeconds) { 280 LocalDateTime now = LocalDateTime.now(); 281 Date lastRunDate = probe.getLastRunnedDate(); 282 LocalDateTime lastRunDateTime = lastRunDate != null ? LocalDateTime.ofInstant(lastRunDate.toInstant(), 283 ZoneId.systemDefault()) : LocalDateTime.MIN; 284 if (ChronoUnit.SECONDS.between(lastRunDateTime, now) > refreshSeconds) { 285 doRunProbe(probe); 286 } 287 } 288 289 private int getDefaultCheckInterval() { 290 return Integer.parseInt(Framework.getProperty(DEFAULT_HEALTH_CHECK_INTERVAL_SECONDS_PROPERTY, 291 DEFAULT_HEALTH_CHECK_INTERVAL_SECONDS)); 292 293 } 294}