/* * Copyright © 2016 Cask Data, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ package co.cask.cdap.common.startup; import co.cask.cdap.common.internal.guava.ClassPath; import com.google.common.base.Predicate; import com.google.inject.Injector; import java.io.IOException; import java.lang.reflect.Modifier; import java.net.URI; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; /** * Runs a collection of {@link Check Checks}. */ public class CheckRunner { private final Set<Check> checks; private CheckRunner(Set<Check> checks) { this.checks = checks; } /** * Runs all checks, returning all the check failures. * * @return list of check failures */ public List<Failure> runChecks() { List<Failure> failures = new ArrayList<>(); for (Check check : checks) { try { check.run(); } catch (Exception e) { failures.add(new Failure(check.getName(), e)); } } return failures; } /** * Create a builder that uses the specified injector to instantiate checks to run. * * @param injector the injector to use to instantiate the checks * @return builder to build a {@link CheckRunner}. */ public static Builder builder(Injector injector) { return new Builder(injector); } /** * Builds a {@link CheckRunner}. */ public static class Builder { private final Set<Check> checks; private final ClassLoader classLoader; private final Injector injector; private ClassPath classPath; public Builder(Injector injector) { this.checks = new HashSet<>(); this.classLoader = this.getClass().getClassLoader(); this.injector = injector; } /** * Recursively search the specified package for any {@link Check Checks} and add them. * * @param pkg the package to search for checks * @return this builder * @throws IOException if there was a problem reading resources from the classpath */ public Builder addChecksInPackage(String pkg) throws IOException { ClassPath classPath = getClassPath(); for (ClassPath.ClassInfo classInfo : classPath.getAllClassesRecursive(pkg)) { Class<?> cls = classInfo.load(); if (!Modifier.isInterface(cls.getModifiers()) && !Modifier.isAbstract(cls.getModifiers()) && Check.class.isAssignableFrom(cls)) { checks.add((Check) injector.getInstance(cls)); } } return this; } /** * Adds the {@link Check} given its class name. * * @param className the class name for the {@link Check}. * @return the builder * @throws ClassNotFoundException if the class could not be found * @throws IllegalArgumentException if the specified class is not a {@link Check}. */ public Builder addClass(String className) throws ClassNotFoundException { Class<?> cls = classLoader.loadClass(className); if (!Check.class.isAssignableFrom(cls)) { throw new IllegalArgumentException(className + " does not implement " + Check.class.getName()); } checks.add((Check) injector.getInstance(cls)); return this; } /** * Build the {@link CheckRunner}. * * @return the {@link CheckRunner} */ public CheckRunner build() { return new CheckRunner(checks); } private ClassPath getClassPath() throws IOException { if (classPath == null) { classPath = ClassPath.from(classLoader, new Predicate<URI>() { @Override public boolean apply(URI input) { // protect against cases where / is in the URLClassLoader uris. // this can happen when the classpath contains empty entries, // and the working directory is the root directory. // this is a fairly common scenario in init scripts. return !"/".equals(input.getPath()); } }); } return classPath; } } /** * Contains the name of a failed check and the exception that caused the failure. */ public static class Failure { private final String name; private final Exception exception; public Failure(String name, Exception exception) { this.name = name; this.exception = exception; } public String getName() { return name; } public Exception getException() { return exception; } } }