Skip to content

Commit

Permalink
When registering a config file, automatically register a health check…
Browse files Browse the repository at this point in the history
… that reflects whether the current config value can be read
  • Loading branch information
petergeneric committed May 27, 2024
1 parent 587bf99 commit 0af8d60
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 8 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.peterphi.std.guice.common;

import com.codahale.metrics.health.HealthCheckRegistry;
import com.google.inject.AbstractModule;
import com.google.inject.Module;
import com.peterphi.std.guice.common.serviceprops.composite.GuiceConfig;
Expand Down Expand Up @@ -51,7 +52,8 @@ protected <T> JAXBResourceProvider<T> bindConfigFile(final Class<T> type, final
*/
protected <T> JAXBResourceProvider<T> bindConfigFile(final Class<T> type, final String propertyName, Consumer<T> onLoad)
{
final JAXBResourceProvider<T> provider = new JAXBResourceProvider<T>(super.getProvider(JAXBResourceFactory.class),
final JAXBResourceProvider<T> provider = new JAXBResourceProvider<T>(getProvider(JAXBResourceFactory.class),
getProvider(HealthCheckRegistry.class),
propertyName,
type,
onLoad);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ public JAXBResourceFactory(GuiceConfig config, JAXBSerialiserFactory factory)
* @param name
* the name of the property
* @param <T>
* @throws RuntimeException if a value cannot be retrieved
*
* @return
*/
Expand All @@ -42,7 +43,7 @@ public synchronized <T> T get(Class<T> clazz, final String name)

if (cached == null)
{
cached = new JAXBNamedResourceFactory<T>(this.config, this.factory, name, clazz);
cached = makeFactory(clazz, name);
cachedReferences.put(name, cached);
}

Expand All @@ -69,7 +70,7 @@ public synchronized <T> T get(Class<T> clazz, final String name, T defaultValue)

if (cached == null)
{
cached = new JAXBNamedResourceFactory<T>(this.config, this.factory, name, clazz);
cached = makeFactory(clazz, name);
cachedReferences.put(name, cached);
}

Expand All @@ -83,11 +84,16 @@ public synchronized <T> T get(Class<T> clazz, final String name, T defaultValue)
* @param clazz
* @param name
* @param <T>
*
* @return
*/
public <T> T getOnce(final Class<T> clazz, final String name)
{
return new JAXBNamedResourceFactory<T>(this.config, this.factory, name, clazz).get();
return makeFactory(clazz, name).get();
}


private <T> JAXBNamedResourceFactory<T> makeFactory(final Class<T> clazz, final String name)
{
return new JAXBNamedResourceFactory<T>(this.config, this.factory, name, clazz);
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.peterphi.std.guice.common.serviceprops.jaxbref;

import com.codahale.metrics.health.HealthCheck;
import com.codahale.metrics.health.HealthCheckRegistry;
import com.google.inject.Provider;
import com.peterphi.std.threading.Timeout;

Expand All @@ -8,6 +10,7 @@
public class JAXBResourceProvider<T> implements Provider<T>
{
private final Provider<JAXBResourceFactory> resourceFactoryProvider;
private final Provider<HealthCheckRegistry> healthCheckRegistryProvider;
private final String propertyName;
private final Class<T> clazz;
private final Consumer<T> onLoadMethod;
Expand All @@ -17,13 +20,17 @@ public class JAXBResourceProvider<T> implements Provider<T>
private volatile T value;
private volatile long cacheExpires = Integer.MIN_VALUE;

private boolean hasRegisteredHealthCheck = false;


public JAXBResourceProvider(final Provider<JAXBResourceFactory> resourceFactoryProvider,
final Provider<HealthCheckRegistry> healthCheckRegistryProvider,
final String propertyName,
final Class<T> clazz,
final Consumer<T> onLoadMethod)
{
this.resourceFactoryProvider = resourceFactoryProvider;
this.healthCheckRegistryProvider = healthCheckRegistryProvider;
this.propertyName = propertyName;
this.clazz = clazz;
this.onLoadMethod = onLoadMethod;
Expand Down Expand Up @@ -55,6 +62,14 @@ public synchronized T get()
return obj;
}

final T obj = load(now);

return obj;
}


private synchronized T load(final long now)
{
// Cannot use cache, must recompute
final T obj = deserialise();

Expand All @@ -64,13 +79,43 @@ public synchronized T get()
this.value = obj;
this.cacheExpires = now + cacheValidity.getMilliseconds();
}

return obj;
}

private T deserialise()

private synchronized T deserialise()
{
final T obj = resourceFactoryProvider.get().getOnce(clazz, propertyName);
final JAXBResourceFactory provider = resourceFactoryProvider.get();

if (!hasRegisteredHealthCheck)
{
// Make sure we only register (or try to register) once
hasRegisteredHealthCheck = true;

// N.B. manually constructing a name of the form used by AbstractHealthCheck (which is not available in this module) so errors appear with the appropriate severity
healthCheckRegistryProvider.get().register("COMPROMISED:ConfigFile." + propertyName, new HealthCheck()
{
@Override
protected Result check() throws Exception
{
try
{
// Try to reload the live value on disk
// We call this because we want the actual value, not the last successfully loaded value
// N.B. this will also populate the cache on successful load
load(System.currentTimeMillis());

return Result.healthy();
}
catch (Throwable t)
{
return Result.unhealthy(t);
}
}
});
}

final T obj = provider.getOnce(clazz, propertyName);

if (onLoadMethod != null)
onLoadMethod.accept(obj);
Expand Down

0 comments on commit 0af8d60

Please sign in to comment.