From b873f971872f792ccd0d51a714e114c08a00bf30 Mon Sep 17 00:00:00 2001 From: lucio Date: Wed, 1 Feb 2023 14:40:31 +0100 Subject: [PATCH] Health extension implemented --- pom.xml | 6 +- .../application/ApplicationContext.java | 2 +- .../DefaultApplicationContext.java | 5 +- .../context/container/ContainerContext.java | 1 + .../smartgears/extensions/ApiResource.java | 2 +- .../extensions/ApplicationExtension.java | 2 + .../smartgears/extensions/HttpExtension.java | 3 + .../extensions/resource/HealthResource.java | 83 ++++++- .../smartgears/health/HealthManager.java | 18 ++ .../smartgears/health/HealthResponse.java | 35 +++ .../gcube/smartgears/health/HealthTask.java | 43 ++++ .../health/KeyCloakHealthCheck.java | 4 + .../managers/ApplicationManager.java | 19 +- .../smartgears/provider/DefaultProvider.java | 206 ++++++++++-------- 14 files changed, 318 insertions(+), 111 deletions(-) create mode 100644 src/main/java/org/gcube/smartgears/health/HealthManager.java create mode 100644 src/main/java/org/gcube/smartgears/health/HealthResponse.java create mode 100644 src/main/java/org/gcube/smartgears/health/HealthTask.java diff --git a/pom.xml b/pom.xml index d929d94..7320a92 100644 --- a/pom.xml +++ b/pom.xml @@ -38,9 +38,9 @@ - org.reflections - reflections - 0.9.10 + io.github.classgraph + classgraph + 4.8.28 org.gcube.common diff --git a/src/main/java/org/gcube/smartgears/context/application/ApplicationContext.java b/src/main/java/org/gcube/smartgears/context/application/ApplicationContext.java index 399cead..949c420 100644 --- a/src/main/java/org/gcube/smartgears/context/application/ApplicationContext.java +++ b/src/main/java/org/gcube/smartgears/context/application/ApplicationContext.java @@ -82,5 +82,5 @@ public interface ApplicationContext { * @return the AuhtorizationProvider **/ AuthorizationProvider authorizationProvider(); - + } diff --git a/src/main/java/org/gcube/smartgears/context/application/DefaultApplicationContext.java b/src/main/java/org/gcube/smartgears/context/application/DefaultApplicationContext.java index 386d330..4e0b443 100644 --- a/src/main/java/org/gcube/smartgears/context/application/DefaultApplicationContext.java +++ b/src/main/java/org/gcube/smartgears/context/application/DefaultApplicationContext.java @@ -6,6 +6,7 @@ import org.gcube.common.events.Hub; import org.gcube.smartgears.configuration.application.ApplicationConfiguration; import org.gcube.smartgears.context.Properties; import org.gcube.smartgears.context.container.ContainerContext; +import org.gcube.smartgears.health.HealthManager; import org.gcube.smartgears.lifecycle.application.ApplicationLifecycle; import org.gcube.smartgears.persistence.PersistenceWriter; import org.gcube.smartgears.security.AuthorizationProvider; @@ -42,7 +43,7 @@ public class DefaultApplicationContext implements ApplicationContext { this.configuration=configuration; this.hub=hub; this.lifecycle = lifecycle; - this.properties=properties; + this.properties=properties; } /** @@ -106,6 +107,6 @@ public class DefaultApplicationContext implements ApplicationContext { **/ public AuthorizationProvider authorizationProvider() { return container().authorizationProvider(); - } + } } diff --git a/src/main/java/org/gcube/smartgears/context/container/ContainerContext.java b/src/main/java/org/gcube/smartgears/context/container/ContainerContext.java index 9cc70da..454a94a 100644 --- a/src/main/java/org/gcube/smartgears/context/container/ContainerContext.java +++ b/src/main/java/org/gcube/smartgears/context/container/ContainerContext.java @@ -51,4 +51,5 @@ public interface ContainerContext { AuthorizationProvider authorizationProvider(); + } diff --git a/src/main/java/org/gcube/smartgears/extensions/ApiResource.java b/src/main/java/org/gcube/smartgears/extensions/ApiResource.java index 6fab2bd..a49eb27 100644 --- a/src/main/java/org/gcube/smartgears/extensions/ApiResource.java +++ b/src/main/java/org/gcube/smartgears/extensions/ApiResource.java @@ -56,7 +56,7 @@ public abstract class ApiResource extends HttpExtension { public Set excludes() { return Collections.singleton(new Exclude(Constants.root_mapping+mapping())); } - + /** * Returns true if this resource supports a given method. * @param method the method diff --git a/src/main/java/org/gcube/smartgears/extensions/ApplicationExtension.java b/src/main/java/org/gcube/smartgears/extensions/ApplicationExtension.java index c4216d6..9658b4d 100644 --- a/src/main/java/org/gcube/smartgears/extensions/ApplicationExtension.java +++ b/src/main/java/org/gcube/smartgears/extensions/ApplicationExtension.java @@ -22,6 +22,8 @@ public interface ApplicationExtension extends Servlet { */ void init(ApplicationContext context) throws Exception; + void stop(); + /** * Returns the name of this extension. * @return the name diff --git a/src/main/java/org/gcube/smartgears/extensions/HttpExtension.java b/src/main/java/org/gcube/smartgears/extensions/HttpExtension.java index d8edea6..f3660d2 100644 --- a/src/main/java/org/gcube/smartgears/extensions/HttpExtension.java +++ b/src/main/java/org/gcube/smartgears/extensions/HttpExtension.java @@ -76,6 +76,9 @@ public abstract class HttpExtension extends HttpServlet implements ApplicationEx this.context=context; } + @Override + public void stop() {} + @Override public Set excludes() { return new HashSet(); //all managed by default diff --git a/src/main/java/org/gcube/smartgears/extensions/resource/HealthResource.java b/src/main/java/org/gcube/smartgears/extensions/resource/HealthResource.java index 2f0b997..ef18a62 100644 --- a/src/main/java/org/gcube/smartgears/extensions/resource/HealthResource.java +++ b/src/main/java/org/gcube/smartgears/extensions/resource/HealthResource.java @@ -4,41 +4,108 @@ import static org.gcube.smartgears.Constants.application_json; import static org.gcube.smartgears.extensions.HttpExtension.Method.GET; import java.io.IOException; +import java.io.PrintWriter; +import java.util.Set; +import java.util.Timer; +import java.util.stream.Collectors; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import org.gcube.common.health.api.response.HealthResponse; +import org.gcube.com.fasterxml.jackson.databind.ObjectMapper; +import org.gcube.common.health.api.HealthCheck; +import org.gcube.common.health.api.ReadinessChecker; +import org.gcube.smartgears.context.application.ApplicationContext; import org.gcube.smartgears.extensions.ApiResource; import org.gcube.smartgears.extensions.ApiSignature; +import org.gcube.smartgears.handlers.application.request.RequestError; +import org.gcube.smartgears.health.HealthManager; +import org.gcube.smartgears.health.HealthResponse; +import org.gcube.smartgears.health.HealthTask; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.github.classgraph.ClassGraph; +import io.github.classgraph.ClassInfo; +import io.github.classgraph.ClassInfoList; +import io.github.classgraph.ScanResult; public class HealthResource extends ApiResource { private static final long serialVersionUID = 1L; + private static Logger log = LoggerFactory.getLogger(HealthResource.class); + public static final String mapping = "/health"; private static final ApiSignature signature = handles(mapping).with(method(GET).produces(application_json)); - + + private HealthManager manager; + + private HealthTask task; + + private Timer timer; HealthResource() { super(signature); } - @Override - public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + public void init(ApplicationContext context) throws Exception { + Set> annotatedReadiness; + + try (ScanResult result = new ClassGraph().enableClassInfo().enableAnnotationInfo().scan()) { + + ClassInfoList classInfos = result.getClassesWithAnnotation(ReadinessChecker.class.getName()); + + annotatedReadiness = classInfos.stream().map(ClassInfo::loadClass) + .filter(c -> HealthCheck.class.isAssignableFrom(c)).collect(Collectors.toSet()); + } + manager = new HealthManager(); + for (Class readnessClass : annotatedReadiness) + try { + manager.register((HealthCheck) readnessClass.getDeclaredConstructor().newInstance()); + log.info("added class {} to health manager", readnessClass.getCanonicalName()); + } catch (Throwable e) { + log.error("healthChecher class {} cannot be instantiated", readnessClass.getCanonicalName(), e); + } + + task = new HealthTask(manager); + + timer = new Timer(true); + + timer.scheduleAtFixedRate(task, 0, 10000); + + super.init(context); } - - public HealthResponse liveness() { - return null; + public void stop() { + timer.cancel(); + } + + @Override + public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + PrintWriter out = resp.getWriter(); + resp.setContentType("application/json"); + resp.setCharacterEncoding("UTF-8"); + Object response = null; + + String contextpath = req.getContextPath(); + log.info("context path is {}", req.getContextPath()); + if (contextpath.endsWith("/readiness")) + response = readiness(); + else + RequestError.resource_notfound_error.fire(); + + new ObjectMapper().writeValue(out, response); + + out.flush(); } public HealthResponse readiness() { - return null; + return task.getResponse(); } } \ No newline at end of file diff --git a/src/main/java/org/gcube/smartgears/health/HealthManager.java b/src/main/java/org/gcube/smartgears/health/HealthManager.java new file mode 100644 index 0000000..64dcab0 --- /dev/null +++ b/src/main/java/org/gcube/smartgears/health/HealthManager.java @@ -0,0 +1,18 @@ +package org.gcube.smartgears.health; + +import java.util.List; + +import org.gcube.common.health.api.HealthCheck; + +public class HealthManager { + + private List checks; + + public void register(HealthCheck check){ + checks.add(check); + } + + public List getChecks() { + return checks; + } +} diff --git a/src/main/java/org/gcube/smartgears/health/HealthResponse.java b/src/main/java/org/gcube/smartgears/health/HealthResponse.java new file mode 100644 index 0000000..c1f9dbd --- /dev/null +++ b/src/main/java/org/gcube/smartgears/health/HealthResponse.java @@ -0,0 +1,35 @@ +package org.gcube.smartgears.health; + +import java.util.List; + +import org.gcube.com.fasterxml.jackson.annotation.JsonInclude; +import org.gcube.com.fasterxml.jackson.annotation.JsonInclude.Include; +import org.gcube.common.health.api.Status; +import org.gcube.common.health.api.response.HealthCheckResponse; +import org.gcube.common.validator.annotations.NotNull; + +@JsonInclude(Include.NON_NULL) +public class HealthResponse { + + @NotNull + private Status status; + + private List checks; + + + + public HealthResponse(Status status, List checks) { + super(); + this.status = status; + this.checks = checks; + } + + public Status getStatus() { + return status; + } + + public List getChecks() { + return checks; + } + +} diff --git a/src/main/java/org/gcube/smartgears/health/HealthTask.java b/src/main/java/org/gcube/smartgears/health/HealthTask.java new file mode 100644 index 0000000..61aa28b --- /dev/null +++ b/src/main/java/org/gcube/smartgears/health/HealthTask.java @@ -0,0 +1,43 @@ +package org.gcube.smartgears.health; + +import java.util.List; +import java.util.TimerTask; +import java.util.stream.Collectors; + +import org.gcube.common.health.api.HealthCheck; +import org.gcube.common.health.api.Status; +import org.gcube.common.health.api.response.HealthCheckResponse; + +public class HealthTask extends TimerTask{ + + private HealthResponse response; + + private HealthManager healthManager; + + public HealthTask(HealthManager healthManager) { + this.healthManager = healthManager; + } + + @Override + public void run() { + List checks = healthManager.getChecks(); + + List responses = checks.stream().map(c -> this.wrap(c)).collect(Collectors.toList()); + Status totalStatus = responses.stream().anyMatch(r -> r.getStatus().equals(Status.DOWN)) ? Status.DOWN : Status.UP; + + this.response = new HealthResponse(totalStatus, responses); + } + + public HealthResponse getResponse() { + return response; + } + + private HealthCheckResponse wrap(HealthCheck check){ + try { + return check.check(); + }catch (Throwable t) { + return HealthCheckResponse.builder(check.getName()).down().withMessage("unexpected error executing check").build(); + } + + } +} diff --git a/src/main/java/org/gcube/smartgears/health/KeyCloakHealthCheck.java b/src/main/java/org/gcube/smartgears/health/KeyCloakHealthCheck.java index 1a78f73..fd087bf 100644 --- a/src/main/java/org/gcube/smartgears/health/KeyCloakHealthCheck.java +++ b/src/main/java/org/gcube/smartgears/health/KeyCloakHealthCheck.java @@ -12,6 +12,10 @@ public class KeyCloakHealthCheck implements HealthCheck{ private static final String CHECK_NAME = "authorization-check" ; + public String getName(){ + return CHECK_NAME; + } + @Override public HealthCheckResponse check() { try { diff --git a/src/main/java/org/gcube/smartgears/managers/ApplicationManager.java b/src/main/java/org/gcube/smartgears/managers/ApplicationManager.java index 4f3bfe1..a1fbe26 100644 --- a/src/main/java/org/gcube/smartgears/managers/ApplicationManager.java +++ b/src/main/java/org/gcube/smartgears/managers/ApplicationManager.java @@ -13,6 +13,7 @@ import java.io.File; import java.io.FileOutputStream; import java.io.ObjectOutputStream; import java.util.Collection; +import java.util.Collections; import java.util.List; import java.util.Map.Entry; @@ -52,6 +53,8 @@ public class ApplicationManager { private ApplicationContext context; + private ApplicationExtensions extensions; + /** * Starts application management. * @@ -80,7 +83,7 @@ public class ApplicationManager { ApplicationHandlers handlers = provider().handlersFor(context); - ApplicationExtensions extensions = provider().extensionsFor(context); + extensions = provider().extensionsFor(context); extensions.validate(); List lifecycleHandlers = handlers.lifecycleHandlers(); @@ -150,7 +153,10 @@ public class ApplicationManager { context.lifecycle().tryMoveTo(stopped); context.events().fire(context, ApplicationLifecycle.stop); - + + if (extensions != null) + unregister(extensions); + stopLifecycleHandlers(); log.info("stopping application events for {}", context.name()); @@ -227,6 +233,15 @@ public class ApplicationManager { } } + + private void unregister(ApplicationExtensions extensions) { + + for (ApplicationExtension extension : extensions.extensions()) + + extension.stop(); + + } + private void start(List handlers) { diff --git a/src/main/java/org/gcube/smartgears/provider/DefaultProvider.java b/src/main/java/org/gcube/smartgears/provider/DefaultProvider.java index 6b092bd..1cf05c0 100644 --- a/src/main/java/org/gcube/smartgears/provider/DefaultProvider.java +++ b/src/main/java/org/gcube/smartgears/provider/DefaultProvider.java @@ -14,12 +14,11 @@ import java.io.File; import java.io.FileInputStream; import java.io.InputStream; import java.io.ObjectInputStream; -import java.net.URL; import java.util.ArrayList; -import java.util.Collection; import java.util.List; import java.util.Set; import java.util.UUID; +import java.util.stream.Collectors; import javax.servlet.ServletContext; @@ -48,14 +47,14 @@ import org.gcube.smartgears.publishing.SmartgearsProfilePublisher; import org.gcube.smartgears.security.AuthorizationProvider; import org.gcube.smartgears.security.AuthorizationProviderFactory; import org.gcube.smartgears.utils.Utils; -import org.reflections.Reflections; -import org.reflections.scanners.SubTypesScanner; -import org.reflections.scanners.TypeAnnotationsScanner; -import org.reflections.util.ClasspathHelper; -import org.reflections.util.ConfigurationBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import io.github.classgraph.ClassGraph; +import io.github.classgraph.ClassInfo; +import io.github.classgraph.ClassInfoList; +import io.github.classgraph.ScanResult; + /** * Default implementation of the {@link Provider} interface. * @@ -67,8 +66,7 @@ public class DefaultProvider implements Provider { private static Logger log = LoggerFactory.getLogger(Provider.class); private ContainerContext containerContext; - //TODO: do the same with applicationContext (with a map) - + // TODO: do the same with applicationContext (with a map) private File configFile = null; @@ -78,13 +76,13 @@ public class DefaultProvider implements Provider { List publishers; - - protected DefaultProvider(){}; + protected DefaultProvider() { + }; @Override public ContainerContext containerContext() { - if(containerContext==null){ + if (containerContext == null) { ContainerConfiguration configuration = containerConfiguration(); Hub hub = new DefaultHub(); @@ -94,27 +92,29 @@ public class DefaultProvider implements Provider { File file = configuration.persistence().file(container_profile_file_path); String id = null; - if (file.exists()){ + if (file.exists()) { log.info("loading persisted state for container"); - try(ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file))){ - id = (String)ois.readObject(); - }catch(Exception e){ - log.error("error loading persisted state, creating new uuid",e); + try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file))) { + id = (String) ois.readObject(); + } catch (Exception e) { + log.error("error loading persisted state, creating new uuid", e); } - } - if (id==null){ + } + if (id == null) { id = UUID.randomUUID().toString(); - log.info("container id created is {}",id); + log.info("container id created is {}", id); - } + } - AuthorizationProviderFactory authfactory = configuration.getauthorizationConfiguration().getAuthProviderFactory(); + AuthorizationProviderFactory authfactory = configuration.getauthorizationConfiguration() + .getAuthProviderFactory(); Credentials credentials = configuration.getauthorizationConfiguration().getCredentials(); - + AuthorizationProvider authProvider = authfactory.connect(credentials); - - containerContext = new DefaultContainerContext(id, configuration, hub, lifecycle, authProvider, new Properties()); + + containerContext = new DefaultContainerContext(id, configuration, hub, lifecycle, authProvider, + new Properties()); } return containerContext; } @@ -127,12 +127,13 @@ public class DefaultProvider implements Provider { ContainerConfigurationBinder binder = new ContainerConfigurationBinder(); ClassLoader currentClassLoader = Thread.currentThread().getContextClassLoader(); - if (currentClassLoader.getParent()!=null && !currentClassLoader.getParent().equals(ClassLoader.getSystemClassLoader())){ + if (currentClassLoader.getParent() != null + && !currentClassLoader.getParent().equals(ClassLoader.getSystemClassLoader())) { log.trace("probably i'm in a webapp classloader"); currentClassLoader = currentClassLoader.getParent(); } - List defaultHandlers = binder.bindHandlers(currentClassLoader); + List defaultHandlers = binder.bindHandlers(currentClassLoader); return defaultHandlers; @@ -150,34 +151,37 @@ public class DefaultProvider implements Provider { ApplicationConfiguration embedded = configurationFor(application); ApplicationConfiguration external = context.configuration().app(application.getContextPath()); - //shouldn't happen: management shouldn't have started at all - if (embedded==null && external==null) - throw new AssertionError("application @ "+application.getContextPath()+" is not distributed with " - + configuration_file_path+" and there is no external configuration for it in "+container_configuraton_file_path); + // shouldn't happen: management shouldn't have started at all + if (embedded == null && external == null) + throw new AssertionError("application @ " + application.getContextPath() + " is not distributed with " + + configuration_file_path + " and there is no external configuration for it in " + + container_configuraton_file_path); - //no embedded configuration + // no embedded configuration if (embedded == null) { - configuration = external ; + configuration = external; - log.info("loaded configuration for application "+configuration.name()+" from "+container_configuraton_file_path); - } - else { + log.info("loaded configuration for application " + configuration.name() + " from " + + container_configuraton_file_path); + } else { configuration = embedded; if (external == null) - log.info("loaded configuration for application "+configuration.name()+" from "+configuration_file_path); + log.info("loaded configuration for application " + configuration.name() + " from " + + configuration_file_path); else { configuration.merge(external); - log.info("loaded configuration for application "+configuration.name()+" from "+configuration_file_path+" and "+container_configuraton_file_path); + log.info("loaded configuration for application " + configuration.name() + " from " + + configuration_file_path + " and " + container_configuraton_file_path); } - } + } ApplicationConfiguration bridgedConfiguration = new BridgedApplicationConfiguration(context.configuration(), configuration); @@ -187,16 +191,16 @@ public class DefaultProvider implements Provider { ApplicationLifecycle lifecycle = new ApplicationLifecycle(hub, configuration.name()); File file = bridgedConfiguration.persistence().file(profile_file_path); - String id= null; - if (file.exists()){ + String id = null; + if (file.exists()) { log.info("loading persisted state for application {}", application.getContextPath()); - try(ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file))){ - id = (String)ois.readObject(); - }catch(Exception e){ - log.error("error loading persisted state, creating new uuid",e); + try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file))) { + id = (String) ois.readObject(); + } catch (Exception e) { + log.error("error loading persisted state, creating new uuid", e); } - } - if (id==null) + } + if (id == null) id = UUID.randomUUID().toString(); return new DefaultApplicationContext(id, context, application, bridgedConfiguration, hub, lifecycle, @@ -208,31 +212,29 @@ public class DefaultProvider implements Provider { try { - ApplicationConfigurationBinder binder = new ApplicationConfigurationBinder(); - - - //searching for smartegars related application handlers in the common classloader + // searching for smartegars related application handlers in the common + // classloader ClassLoader currentClassLoader = Thread.currentThread().getContextClassLoader(); - if (currentClassLoader.getParent()!=null && !currentClassLoader.getParent().equals(ClassLoader.getSystemClassLoader())){ + if (currentClassLoader.getParent() != null + && !currentClassLoader.getParent().equals(ClassLoader.getSystemClassLoader())) { log.trace("probably i'm in a webapp classloader"); currentClassLoader = currentClassLoader.getParent(); } ApplicationHandlers defaultHandlers = binder.bindHandlers(currentClassLoader); - return defaultHandlers; - + return defaultHandlers; } catch (RuntimeException e) { - throw new RuntimeException("cannot install handlers for application @ " + context.name()+" (see cause) ", e); + throw new RuntimeException("cannot install handlers for application @ " + context.name() + " (see cause) ", + e); } } - @Override public ApplicationExtensions extensionsFor(ApplicationContext context) { @@ -248,7 +250,8 @@ public class DefaultProvider implements Provider { config = getClass().getResourceAsStream(default_extensions_file_path); if (config == null) - throw new IllegalStateException("invalid distribution: cannot find " + default_extensions_file_path); + throw new IllegalStateException( + "invalid distribution: cannot find " + default_extensions_file_path); } else log.info("{} uses custom extensions @ {}", context.name(), extensions_file_path); @@ -259,12 +262,12 @@ public class DefaultProvider implements Provider { } catch (RuntimeException e) { - throw new RuntimeException("cannot install extensions for application @ " + context.name()+" (see cause) ", e); + throw new RuntimeException( + "cannot install extensions for application @ " + context.name() + " (see cause) ", e); } } - @Override public SmartGearsConfiguration smartgearsConfiguration() { @@ -297,7 +300,7 @@ public class DefaultProvider implements Provider { InputStream config = application.getResourceAsStream(configuration_file_path); - if (config == null) + if (config == null) return null; ApplicationConfigurationBinder binder = new ApplicationConfigurationBinder(); @@ -313,7 +316,7 @@ public class DefaultProvider implements Provider { private ContainerConfiguration containerConfiguration() { - if (configFile==null) { + if (configFile == null) { String home = Utils.home(); @@ -324,22 +327,24 @@ public class DefaultProvider implements Provider { File homeDir = new File(home); if (!(homeDir.exists() && homeDir.isDirectory() && homeDir.canRead() && homeDir.canWrite())) - throw new IllegalStateException("invalid node configuration: home "+home+" does not exist or is not a directory or cannot be accessed in read/write mode"); - - configFile = new File(homeDir,container_configuraton_file_path); + throw new IllegalStateException("invalid node configuration: home " + home + + " does not exist or is not a directory or cannot be accessed in read/write mode"); + configFile = new File(homeDir, container_configuraton_file_path); log.trace("reading container configuration @ {} ", configFile.getAbsolutePath()); } if (!(configFile.exists() && configFile.canRead())) - throw new IllegalStateException("invalid node configuration: file "+configFile.getAbsolutePath()+" does not exist or cannot be accessed"); + throw new IllegalStateException("invalid node configuration: file " + configFile.getAbsolutePath() + + " does not exist or cannot be accessed"); ContainerConfiguration configuration; - try (InputStream stream = new FileInputStream(configFile)){ - configuration= new ContainerConfigurationBinder().load(stream); - }catch (Exception e) { - throw new IllegalStateException("invalid node configuration: file "+configFile.getAbsolutePath()+" is invalid", e); + try (InputStream stream = new FileInputStream(configFile)) { + configuration = new ContainerConfigurationBinder().load(stream); + } catch (Exception e) { + throw new IllegalStateException( + "invalid node configuration: file " + configFile.getAbsolutePath() + " is invalid", e); } return configuration; @@ -348,43 +353,56 @@ public class DefaultProvider implements Provider { @Override public synchronized List publishers() { if (this.publishers == null) { - //retrieve from root class loader - Collection urls = ClasspathHelper.forClassLoader(Thread.currentThread().getContextClassLoader()); - urls.removeIf(url -> url.toString().endsWith(".so") || url.toString().endsWith(".zip") ); + Set> annotatedPublishers; - ConfigurationBuilder reflectionConf = new ConfigurationBuilder().addUrls(urls).setScanners(new TypeAnnotationsScanner(), new SubTypesScanner()); + try (ScanResult result = new ClassGraph().enableClassInfo().enableAnnotationInfo() + .addClassLoader(Thread.currentThread().getContextClassLoader()).scan()) { - Reflections reflection = new Reflections(reflectionConf); + ClassInfoList classInfos = result.getClassesWithAnnotation(SmartgearsProfilePublisher.class.getName()); + + annotatedPublishers = classInfos.stream().map(ClassInfo::loadClass) + .filter(c -> Publisher.class.isAssignableFrom(c)).collect(Collectors.toSet()); + + } + /* + * Collection urls = + * ClasspathHelper.forClassLoader(Thread.currentThread().getContextClassLoader() + * ); urls.removeIf(url -> url.toString().endsWith(".so") || + * url.toString().endsWith(".zip") ); + * + * + * ConfigurationBuilder reflectionConf = new + * ConfigurationBuilder().addUrls(urls).setScanners(new + * TypeAnnotationsScanner(), new SubTypesScanner()); + * + * Reflections reflection = new Reflections(reflectionConf); + * + * = reflection.getTypesAnnotatedWith(SmartgearsProfilePublisher.class); + */ - Set> annotatedPublishers = reflection.getTypesAnnotatedWith(SmartgearsProfilePublisher.class); List foundPublishers = new ArrayList(); - for (Class annotatedPublisher: annotatedPublishers) { - if (Publisher.class.isAssignableFrom(annotatedPublisher)) - try { - foundPublishers.add((Publisher)annotatedPublisher.newInstance()); - log.info("added class {} to publishers",annotatedPublisher); - } catch (Exception e) { - log.error("publisher class {} cannot be instantiated", annotatedPublisher.getCanonicalName(),e); - } - else - log.warn("publisher class {} discarded, it doesn't implements Publisher class", annotatedPublisher.getCanonicalName()); - + for (Class annotatedPublisher : annotatedPublishers) { + try { + foundPublishers.add((Publisher) annotatedPublisher.getDeclaredConstructor().newInstance()); + log.info("added class {} to publishers", annotatedPublisher); + } catch (Throwable e) { + log.error("publisher class {} cannot be instantiated", annotatedPublisher.getCanonicalName(), e); + } + } this.publishers = foundPublishers; if (foundPublishers.isEmpty()) log.warn("no publishers found in classloader"); } - + return this.publishers; } -/* - @Override - public AuthorizationProvider authorizationProvider() { - return containerContext.authorizationProvider(); - } -*/ - + /* + * @Override public AuthorizationProvider authorizationProvider() { return + * containerContext.authorizationProvider(); } + */ + }